송세연
@songsy4051341
수강평 작성수
-
평균평점
-
블로그
전체 4#카테고리
- 프론트엔드
#태그
- 프론트엔드
- 미션
![[인프런 워밍업 스터디 클럽 1기 프론트엔드] 3주차 발자국](https://cdn.inflearn.com/public/files/blogs/17835f80-1594-4d8e-9207-b3eedde684a0/fd.png?w=260)
2024. 05. 18.
0
[인프런 워밍업 스터디 클럽 1기 프론트엔드] 3주차 발자국
학습 내용Test Driven Development실제 코드를 작성하기 전에 테스트 코드를 먼저 작성한다.테스트 코드를 작성한 후 그 테스트 코드를 Pass할 수 있는 실제 코드를 작성한다.장점많은 기능을 테스트하기 때문에 소스코드에 안정감이 부여된다.실제 개발하면서 많은 시간이 소요되는 부분은 디버깅 부분인데, TDD를 하면 디버깅 시간이 줄어들고 실제 개발 시간도 줄어든다.소스코드 하나하나를 더욱 신중하게 짤 수 있기 때문에 깨끗한 코드가 나올 확률이 높다.리액트 테스팅 라이브러리React 구성 요소에 대한 테스트 작업을 위한 API를 추가하여 DOM Testing Library 위에 구축된 Test 라이브러리DOM Testing Library란 Dom 노드(Node)를 테스트하기 위한 매우 가벼운 솔루션CRA로 생성된 프로젝트는 기본적으로 React Testing Library를 지원한다.Enzyme이 구현 주도 테스트를 지원한다면, React Test Library는 행위 주도 테스트를 지원한다.구현 주도 테스트: 어떤 문자, 태그를 사용해서 구현했는지행위주도 테스트: 어떠한 이벤트를 발생시켰을 때 화면이 어떻게 변화가 되는지Jest페이스북에서 만든 테스팅 프레임워크최소한의 설정으로 동작하며 테스트 케이스를 만들어서 애플리케이션 코드가 잘 돌아가는지를 확인해준다.주로 단위테스트를 위해 이용한다. 붉은 글씨에 해당되는 문자열이 포함된 파일을 테스트 파일로 인식파일 구조&사용법 describe: 여러 관련 테스트를 그룹화하는 블록을 만든다.it: 개별 테스트를 수행하는 곳. 각 테스트를 작은 문장처럼 설명한다.expect(): 값을 테스트할 때마다 사용된다. matcher와 함께 사용된다.matcher(): 다른 방법으로 값을 테스트하기 위해 사용한다.test('two plus two is four',()=>{ expect(2 + 2).toBe(4) } render(): DOM에 컴포넌트를 렌더링하는 함수. 인자로 렌더링할 React 컴포넌트가 들어간다.쿼리 함수페이지에서 요소를 찾기 위해 테스트 라이브러리가 제공한느 방법.여러 유형의 쿼리(get, find, query)가 있으며, 이들 간의 차이점은 요소가 발견되지 않으면 쿼리에서 오류가 발생하는지 또는 Promise를 반환하고 다시 시도하는지 여부이다.NextJSReact의 서버 사이드 렌더링을 쉽게 구현할 수 있게 도와주는 간단한 프레임워크.리액트로 개발할 때 SPA를 이용하여 클라이언트 사이드 렌더링을 하기 때문에 좋은 점도 있지만 검색엔진 최적화(SEO) 면에서는 좋지 않다.클라이언트 사이드 렌더링을 하면 첫 페이지에서 빈 html을 가져오고 js 파일을 해석해 화면을 구성하기 때문에, 포털 검색에 거의 노출될 일이 없다.Next.js는 Pre-Rendering을 통해 페이지를 미리 렌더링하고 완성된 HTML을 가져오기 때문에 사용자와 검색엔진 크롤러에게 바로 렌더링된 페이지를 전달할 수 있다.Server Side Rendering클라이언트 대신 서버에서 페이지를 준비하는 원리기존 리액트에서는 CSR을 하기 때문에 서버에 영향을 미치지 않고, 서버에서 클라이언트로 응답해서 보낸 html도 거의 비어있다.⇒ 이 방식은 서버에서 데이터를 가져올 때 지연 시간 발생으로 UX 측면에서 좋지 않을 수 있다.⇒ 검색 엔진에 검색 시 웹 크롤링이 동작할 때 내용을 제대로 가져와 읽을 수 없기에 검색엔진 최적화에 문제가 된다.Next.js 앱 생성 방법npx create-next-app [경로]Next.js 프로젝트 구성pages: 이 폴더 안에 페이지들을 생성한다.처음엔 index.tsx가 “/” 페이지로 매핑된 상태app.tsx는 공통되는 레이아웃을 작성한다. 모든 페이지에 공통으로 들어가는 걸 넣어주려면 여기에 넣어주면 된다.(헤더, Footer 등)⇒url을 통해 특정 페이지에 진입하기 전 통과하는 일종의 인터셉터 페이지.public: 이미지같은 정적 에셋들을 보관한다.styles: 스타일을 처리해주는 폴더. 모듈(module) css는 컴포넌트 종속적으로 스타일링하기 위한 것으로, 확장자 앞에 module을 붙여주어야 한다.next.config.js: Nextjs는 웹팩을 기본 번들러로 사용한다. 그래서 웹팩에 관한 설정들을 이 파일에서 해줄 수 있다.Pre-rendering모든 페이지를 위한 HTML을 클라이언트 사이드에서 자바스크립트로 처리하기 전, ‘사전에’ 생성하는 것.이 pre-rendering 덕분에 SEO 검색엔진 최적화가 좋아진다.브라우저에서 JavaScript를 사용하지 못하게 해놓으면 Pre-Rendering이 이루어지는지, 아닌지 확인할 수 있다.Data Fetching리액트에서 데이터를 가져올 때는 useEffect 안에서 가져온다.Next.js는 다음의 세 가지 방법을 통해 데이터를 가져온다.getStaticProps: Static Generation으로 빌드(build)할 때 데이터를 불러옴(미리 만들어줌)사용하는 경우페이지를 렌더링하는 데 필요한 데이터는 사용자의 요청보다 먼저 build 시간에 필요한 데이터를 가져올 때Headless CMS에서 데이터를 가져올 때데이터를 공개적으로 캐시할 수 있을 때(사용자별 아님)페이지가 미리 렌더링되어야 하고 매우 빨라야 할 때getStaticPaths: static generation으로 데이터에 기반하여 pre-render 시 특정한 동적 라우팅 구현(pages/post/[id].js)getServerSideProps: 서버 사이드 렌더링으로 요청이 있을 때 데이터를 불러옴사용하는 경우요청할 때 데이터를 가져와야 하는 페이지를 미리 렌더해야 할 때 사용서버가 모든 요청에 대한 결과를 계산하고, 추가 구성없이 CDN에 의해 결과를 캐시할 수 없기 때문에 첫번째 바이트까지의 시간은 getStaticProps보다 느리다.type assertion(타입 표명)시스템이 추론/분석한 타입 내용을 우리가 얼마든지 바꿀 수 있는데, 이 때 사용되는 메커니즘이 type assertion이다.type assertion을 사용하면 값의 type을 설정하고 컴파일러가 이를 유추하지 않도록 지시할 수 있다.var foo = {}; foo.bar = 123; //오류interface Foo{ bar: number; bas: string; } var foo = {} as Foo; too.bar = 123; foo.bas = 'hello';타입 표명은 as Foo, 두 가지 방식으로 표현 가능한데, 리액트에서는 JSX문법과 겹치기 때문에 보다는 as Foo를 공통적으로 사용하는 것이 권장된다.학습 회고짧다면 짧고, 길다면 길었던 인프런 워밍업 클럽 1기 활동이 끝났다. 3주동안 30여 시간의 강의를 듣는것은 생각보다 만만치 않았고, 깊은 공부를 하기엔 조금 템포가 빠르지 않았나 싶다. 물론 이건 내가 병행하고 있는 일들이 많아서 상대적으로 그렇게 느껴진 것 같기도 하다.이번 인프런 워밍업 클럽 활동을 통해 새로운 지식을 얻었을 뿐만 아니라 미션을 통해 내 역량을 확인하고 앞으로의 학습 방향성에 대해 고민해볼 수 있어 좋았다.이번 주차 학습 과정에서 Next.js를 처음 접해보았는데, 미션에서 활용하지는 못해서 나중에 다른 프로젝트로 Next.js를 도입해보고 싶다.미션 해결과정노트 앱 미션을 구현하는 과정 중 노트 정렬 기능을 구현하는 데 삽질을 했던 게 기억이 난다.분명 콘솔을 통해 정렬 로직이 잘 동작하는 것을 확인을 했는데, 실제로 노트 정렬 순서가 업데이트되지 않아서(렌더링되지 않아서) 고생을 했는데, 문제 원인은 정렬 기준에 따른 분기문(switch문)에서 case별로 break를 걸어주지 않아 제대로 적용이 안된 것이었다.switch문에서 break를 쓰는 건 기본 중의 기본인데, 이런 실수를 한 게 어이가 없었다.디버깅이란 언제나 참 어려운 것 같다.미션 회고난이도 상 미션이라 그런지 자바스크립트 미션에 비해 구현해야 할 내용이 상당히 많아서 꽤 고생스러웠던 것 같다.특히, 상태를 어떻게 관리할지 설계하는 과정이 무척 어렵게 느껴졌다.기능을 구현하는 것은 쉽지만, 코드를 깔끔하고 수정하기 쉽게 만드는 건 정말 어려운 것 같다.
프론트엔드
![[인프런 워밍업 스터디 클럽 1기 프론트엔드] 미션 제출](https://cdn.inflearn.com/public/files/blogs/014bb82a-c7f2-4e8d-a8d7-e1dcef1f4fea/fd.png?w=260)
2024. 05. 18.
0
[인프런 워밍업 스터디 클럽 1기 프론트엔드] 미션 제출
1번 과제 (Day5) (책 리스트 나열 앱)깃허브 저장소 주소: https://github.com/amaran-th/infrean-warmup-club/tree/main/javascript/book_list2번 과제 (Day5) (Github Finder 앱)깃허브 저장소 주소: https://github.com/amaran-th/infrean-warmup-club/tree/main/javascript/github_search3번 과제 (Day7) (타이핑 테스트 앱)깃허브 저장소 주소: https://github.com/amaran-th/infrean-warmup-club/tree/main/javascript/typing_test4번 과제 (Day9) (예산 계산기 앱)깃허브 저장소 주소: https://github.com/amaran-th/infrean-warmup-club/tree/main/react5번 과제 (Day11) (포켓몬 도감 앱)깃허브 저장소 주소: https://github.com/amaran-th/infrean-warmup-club/tree/main/react6번 과제 (Day13) (노트 앱)깃허브 저장소 주소: https://github.com/amaran-th/infrean-warmup-club/tree/main/react
프론트엔드
・
프론트엔드
・
미션
![[인프런 워밍업 스터디 클럽 1기 프론트엔드] 2주차 발자국](https://cdn.inflearn.com/public/files/blogs/fd0daa5f-9b6c-4239-8246-af3beeb0b8df/fd.png?w=260)
2024. 05. 12.
0
[인프런 워밍업 스터디 클럽 1기 프론트엔드] 2주차 발자국
학습 내용리액트란Vue와 Angular는 프레임워크, 리액트는 라이브러리리액트가 라이브러리인 이유?리액트는 전적으로 UI를 렌더링하는 데 관여하기 때문.가상 돔: 실제 돔을 메모리에 복사해준 것기존 웹 앱의 경우 인터랙션이 발생할 때마다 렌더트리를 다시 생성하고 렌더링 과정을 다시 거치게 됨.인터랙션이 많이 일어나는 웹의 경우 불필요하게 DOM을 조작하는 비용이 너무 커진다.업데이트 전 후의 가상돔을 비교해 변경된 부분을 찾고(Diffing) 변경된 부분만 실제 돔에 적용한다.(reconciliation)webpack여러 파일의 자바스크립트 코드를 압축해 최적화할 수 있다.⇒ 로딩에 대한 네트워크 비용 절감모듈 단위의 개발이 가능, 가독성이 좋고 유지보수가 쉽다.babel: 최신 자바스크립트 문법을 지원하지 않는 브라우저를 위해 최신 자바스크립트 문법을 구형 브라우저에서도 돌 수 있도록 변환시켜주는 라이브러리.npx: 노드 패키지 실행을 도와주는 도구SPA(Single Page Application)리액트는 멀티페이지 어플리케이션이 아니라, 템플릿 하나를 이용해 동적으로 화면을 전환하는 SPA이다.JSX(Javascript Syntax eXtension)리액트에서 사용하는 자바스크립트의 확장 문법. 자바스크립트와 HTML 문법이 결합된 형태.원래 리액트에서 화면을 그리는 방식: React.createElement API 를 사용해 element를 생성하고 이 element를 in-memory에 저장한다. 그리고 RenderDOM.render() 함수를 사용해 실제 웹 브라우저에 그려준다.⇒불편하다!JSX로 작성한 코드는 babel에 의해 createElement로 변환된다.JSX Key 속성가상 돔을 구축할 때 key를 이용해서 어떤 부분이 변경되었는지를 인식할 수 있다.key로 index를 사용하는 것이 권장되지 않는 이유⇒리스트 요소가 추가되거나 제거되면 해당 리스트들의 key값도 바뀌게 되는데 새로 맨 앞에 들어온 요소가 그 전에 있던 index를 key값으로 가져서 원하는 대로 동작하지 않을 수 있다.React Hooks바벨을 이용해서 클래스 컴포넌트와 함수형 컴포넌트를 변환시키면 함수형 컴포넌트가 월등히 코드량이 적다.함수형 컴포넌트가 더 간결하고 성능도 좋다.HOC(Higher Order Component) 컴포넌트 : 화면에서 재사용 가능한 로직만을 분리해서 component로 만들고, 재사용 불가능한 UI와 같은 다른 부분들은 parameter로 받아서 처리하는 방법.⇒컴포넌트를 인자로 받아서 새로운 리액트 컴포넌트를 리턴하는 함수너무나 많은 HOC를 사용하면 wrapper가 너무 많아진다. React Hooks는 HOC 대신 Custom Hooks를 이용해 컴포넌트를 만들고 처리를 한다. 그로 인해 Wrapper가 많아지는 일을 방지할 수 있다.생명주기를 위해 componentDidMount, componentDidUpdate, componentWillUnmount를 사용했던 클래스 컴포넌트와 달리 Hooks는 useEffect 하나로 간단하게 생명주기를 처리할 수 있다.State와 Propsstate: 컴포넌트 내부에서 데이터를 전달할 때 사용. 변경 가능. State가 변하면 리렌더링된다.props: 부모 컴포넌트로부터 자식컴포넌트로 데이터를 전달할 때 사용. 변경 불가.react-beautiful-dnd: 리액트에서 드래그앤드랍 기능을 편하게 구현할 수 있게 해주는 모듈리액트에서 불변성이란: 값이나 상태를 변경할 수 없는 것.원시타입은 불변성을 가진다. 참조타입은 불변성이 지켜지지 않는다.불변성을 지켜야 하는 이유는 참조 타입에서 객체나 배열의 값이 변할 때 원본 데이터가 변경되어 이 원본 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있어서이다.⇒프로그램의 복잡도 상승리액트에서 화면을 업데이트할 땐 값을 이전 값과 비교해서 변경된 사항을 확인한 후 업데이트하는데, 불변성이 지켜지지 않을 경우 비교할 대상인 이전 값도 함께 변경되기 때문에 불변성이 지켜져야 한다.불변성을 지키기 위해서는 값을 바꾸는 것이 아닌 새로운 배열을 생성해서 할당하는 방식으로 프로그래밍하면 된다.useCallback을 이용한 함수 최적화컴포넌트가 렌더링될 때 그 안에 있는 함수도 다시 생성되는데, 만약 함수가 자식컴포넌트에 props로 전달된다면 함수를 포함하고 있는 컴포넌트(부모 컴포넌트)가 리렌더링될 때마다 자식컴포넌트도 함수가 새롭게 만들어지기 때문에 계속 리렌더링하게 된다.styled components: javascript 파일 안에서 CSS를 처리할 수 있게 도와주는 라이브러리Iframe : HTML Inline Frame 요소로, inline frame의 약자.효과적으로 다른 HTML 페이지를 현재 페이지에 포함시키는 중첩된 브라우저.Iframe 요소를 이용하면 해당 웹 페이지 안에 어떠한 제한 없이 다른 페이지를 불러와서 삽입할 수 있다.scrollLeftElement.scrollLeft 속성은 요소의 콘텐츠가 왼쪽 가장자리에서 스크롤되는 픽셀 수를 가져오거나 설정한다. 학습 회고이번 주차동안 수강했던 강의 내용엔 내가 이미 알고 있던 것도 있었지만 잘 몰랐던 지식도 새롭게 접할 수 있어 좋았다.특히 리액트 강의의 섹션 3~4는 간단한 TODO 앱을 만들어보는 구현 파트와 여러 도구를 활용해 코드를 최적화하는 최적화 파트로 구성되어 있었는데, 어떻게 하면 내가 만든 앱의 성능을 최적화하고 수정하기 쉬운 코드로 만들 수 있을지를 고민해볼 수 있어서 좋았다.인프런 워밍업 클럽의 강의는 내가 학습할 수 있는 주제를 끊임없이 던져주는 것 같다. 인프런 워밍업 클럽이 끝난 뒤 내게 남은 과제는 이렇게 모아둔 학습 주제들에 심층적으로 파고들어 탄탄한 지식의 기반을 쌓는 일일 것이다.남은 한 주도 성실하게 임해서 잘 마무리 지을 수 있으면 좋겠다. 미션 해결 과정이번 주부터 리액트 미션을 시작했다. 레포를 어떻게 관리할까 고민하다가, 하나의 Home 페이지를 만들고 3개의 버튼을 두어 버튼을 클릭하면 각 미션의 결과물이 나오도록 만들기로 했다.이번 주에 완수한 미션은 ‘포켓몬 도감 앱’이다.기본적으로 https://pokeapi.co/에서 제공해주는 api를 사용해 포켓몬 데이터를 불러오고 화면에 띄우는 방식으로 구현했다.import axios from 'axios'; const PokedexInstance = axios.create({ baseURL: '', }); export default PokedexInstance; baseURL을 설정한 axios 객체를 만들고import PokedexInstance from './axios'; const findPokemons = async (targetOffset) => { try { const queryParams = new URLSearchParams({ limit: '20', offset: `${targetOffset}`, }); const res = await PokedexInstance.get(`/pokemon?${queryParams.toString()}`); const detailRes = await Promise.all( res.data.results.map((result) => { return PokedexInstance.get(result.url); }), ); return res.data.results.map((data, i) => { data = detailRes[i].data; return data; }); } catch (e) { console.error(e); throw e; } }; 포켓몬 목록을 불러오는 함수를 작성한다.포켓몬 목록을 조회할 때는 각 포켓몬의 디테일한 정보가 없고, 포켓몬의 정보를 얻을 수 있는 api 주소가 제공된다.그래서 다시 포켓몬별로 api를 일일이 호출해주어야 하는데, 이를 최적화하기 위해 Promise.all() 함수를 사용했다.Promise.all()을 사용하면 여러 개의 로직을 수행할 때 비동기적으로 수행하여 총 수행 시간을 절약할 수 있다.이 다음으로는 적절한 UI를 구성하고, 불러온 데이터를 화면에 띄우기만 하면 되었다. 미션 회고원래도 포켓몬을 좋아해서 리액트 첫 미션으로 포켓몬 도감 앱을 선택했는데, 생각보다 볼륨이 커서 오래 걸렸다.참고 영상에 나와있는 것과 유사하게 구현하려고 노력했는데, 포켓몬의 타입 별 색상을 맞추느라 삽질을 좀 했다. api에서 제공해주는 것이라 생각해서 계속 찾아보다가, 끝내 찾지 못해 하드코딩으로 색상을 적용해주었다. 아마 api로 제공되지 않는 정보인 것 같다.리액트 앱을 만들긴 정말 오랜만인데, 이번 미션을 통해 내 코딩 습관을 조금 돌아보게 된 것 같다.나는 보통 클린코드나 설계같은 걸 고려하지 않고 통으로 코드를 짜고, 기능 구현이 완료된 뒤에야 리팩토링을 하는 편이다. 작은 앱이라면 괜찮은데, 프로젝트의 규모가 커질수록 잘못된 설계로 인해 전체적인 코드 복잡도가 높아지고, 가독성도 나빠 좋지 못한 코드가 되는 것 같다.다음 미션부터는 컴포넌트 단위로 쪼개고 상태를 어떻게 관리할지를 천천히 설계해봐야겠다.
프론트엔드
![[인프런 워밍업 스터디 클럽 1기 프론트엔드] 1주차 발자국](https://cdn.inflearn.com/public/files/blogs/e076c13b-75f5-40db-a0c7-9629f57db2d0/fd.png?w=260)
2024. 05. 04.
0
[인프런 워밍업 스터디 클럽 1기 프론트엔드] 1주차 발자국
학습 내용호이스팅이번에 강의를 통해 호이스팅이라는 걸 처음 알게 됐는데, 꽤 흥미로운 개념이었다. 그런데 실제 예제를 찾아보니 내가 제대로 이해하고 있지 않은 것 같다는 생각이 들어서, 관련 내용을 좀 더 찾아보게 되었다.// 1. let a; console.log(a); a = 3; // 2. console.log(a); let a = 3; // 3. console.log(a); var a = 3; 1, 3번의 경우 undefined가 출력되고, 2번의 경우 참조 에러가 발생한다. 왜 이런 결과가 나오는지를 이해하고 설명할 수 있다면 당신은 호이스팅에 대해 제대로 이해한 것이다.자바스크립트를 실행시키는 인터프리터는 변수를 생성하는 단계를 선언단계와 할당 단계로 분할한다. 코드를 실행하기 전 변수의 선언 부분은 현재 범위의 맨 위로 호이스팅 된다. 이 때, 호이스팅된 var 변수는 undefined 값이 할당된다. 이것이 3번 예제 코드가 undefined를 출력하는 이유이다. 반면, 호이스팅된 let, const 변수는 값을 할당받지 않고, TDZ 상태에 있게 된다. TDZ(Temporal Dead Zone)란 일시적 데드 존으로, 변수를 사용할 수 없는 일시적인 비활성 상태를 의미한다. 이 상태에서 변수에 접근하게 되면 참조 에러를 발생시킨다. 이것이 2번 예제 코드에서 참조 에러가 발생하는 이유이다.클래스도 let, const 변수와 동일한 메커니즘이 적용된다.마지막으로 함수의 경우에도 동일하게 호이스팅 개념이 적용된다.hello(); function hello(){ console.log("hello"); } 위와 같은 함수 선언문의 경우, 선언문 자체가 호이스팅되므로 함수 선언 전에 함수를 호출해도 정상적으로 함수를 호출할 수 있다.hello(); var hello = ()=> { console.log("hello"); } 반면 위와 같은 함수 표현식의 경우, 선언문(var hello;)만 호이스팅되므로 함수 선언 전에 함수를 호출하면 참조 에러가 발생하게 된다.CRP섹션 2에서 잠시 언급되었던 CRP(Critical Rendering Path)라는 개념이 흥미로워서 개인 블로그에 따로 정리해보았다. 게시글 링크학습 회고이번에 인프런 워밍업 클럽 스터디에 참여하면서, 내가 앞으로 공부해야 할 개념들을 많이 접할 수 있어 좋았다. 지금까지 알게된 내용을 다 정리하기엔 아직 시간이 더 필요할 것 같지만, 지금은 내게 이 과정이 계속해서 동기부여가 되어준단 것으로 충분한 것 같다.미션 중 고민했던 것5일차 미션인 Github Finder 앱을 구현하면서 한 가지 고민했던 요소가 있다. Github API를 사용할 때 사용되는 Github Token의 경우 내 명의나 다름이 없어서 남들에게 유출되면 곤란한 정보인데, 이걸 js 코드에 직접적으로 드러내지 않으면서 Live Server로 띄울 수 있게 하는 방법을 찾고 싶었다. 토큰을 .env 파일에 저장하니 Live Server로 앱을 띄웠을 때 .env 파일의 경로를 찾지 못했다.며칠간 방법을 고민해보았는데, 사실 아직도 뾰족한 수는 찾지 못했다. Live Server로 띄우려면 어쩔 수 없는 걸까…7일차 미션인 타이핑 테스트 앱을 구현하면서 가장 머리를 싸맸던 부분은 타자 입력 결과를 출력할 때 맞게 입력한 문자와 오타 문자에 각각의 스타일을 적용하는 것이었다.이 사진처럼, 예제 문자열을 사용자가 입력한 문자열과 비교하고 맞게 입력한 문자는 초록색, 오타 문자는 빨간색으로 스타일을 지정해주어야 했다.문자 하나하나를 태그로 묶는 것은 너무 투박한 방식인 것 같아서, 같은 상태의 연속된 문자를 하나의 태그에 묶는 방식으로 구현하기로 했다.이 방식대로라면 위의 입력 결과는 다음과 같은 html element로 표현되어야 한다.he realized what was happenin g and told the others. 연속해서 같은 상태의 문자가 나타나는 경우와 그렇지 않는 경우로 나누어 로직을 처리했다.for (let i = 0; i 이 문제를 해결하고 나니 이런 동적인 로직을 잘 구현하려면 DOM 요소를 처리하는 과정을 머릿속으로 잘 그릴 수 있어야겠다는 생각이 들었다.주차 회고학교 생활과 병행하려니 생각보다 커리큘럼이 많이 벅차서, 시간관리에 좀더 신경을 써야할 것 같다. 그럼에도 첫 주차를 잘 마무리한 나 자신을 칭찬하고 싶다.마지막으로 프론트엔드를 공부했던 게 제작년 겨울이었는데, 그 사이 잊어버렸던 자바스크립트 개념들이 강의를 들으며 새록새록 떠올라서 좋았다.부족한 기본기를 채우고자 시작한 활동이었는데, 강의 컨텐츠도 매우 만족스럽고 기존에 몰랐던 새로운 내용을 배워 나가는 게 즐겁다.남은 기간동안에도 끝까지 포기하지 않고 잘 마무리지을수 있도록 열심히 해야겠다.
프론트엔드




