블로그

장서윤

[인프런 워밍업 클럽 0기] 2주차 발자국 👣

이번 발자국은 과제 진행 과정을 작성해 볼 예정이다. 목차는 다음과 같다.1. 기능 목록 작성2. 공통 컴포넌트 구현3. 결과물4. 고민했던 부분 ✅️ 1. 기능 목록 작성1. 항목 추가/수정할 수 있다.지출 항목 + 비용을 입력받는다 (조건을 만족할 때까지)지출 항목은 문자열이여야 한다.비용은 숫자여야 한다.지출 항목 + 비용 모두 입력되어야 한다. (공백 제외)  조건을 만족할 경우, 버튼이 enable 된다. 추가/수정을 완료했을 경우, toast 메시지를 띄워준다. 2. 항목 리스트를 보여준다. (테이블 형식)각 항목(row) 마다 수정/삭제 버튼이 존재한다.수정 버튼을 누른 경우[ 기능목록 1. 항목 추가/수정할 수 있다 ] 로 이동한다.삭제 버튼을 누른 경우항목이 삭제되며, toast 메시지를 띄워준다. 총 지출 금액을 보여준다.항목 리스트가 없을 경우, '존재하지 않습니다' 를 보여준다.   3. 모든 항목을 삭제할 수 있다.목록 지우기 버튼을 누른 경우모든 항목이 삭제되며, toast 메시지를 띄워준다. ✅ 2. 공통 컴포넌트 구현MUI와 같은 UI 라이브러리를 쓰지 않고, tailwind css로만 구현할 것이기에, 필요한 컴포넌트는 직접 만들어야한다. 먼저 공통된 디자인을 위해 rounded만을 사용하고자 했다. ( 컴포넌트마다 rounded를 다 다르게 사용하지 않는다)색상은 emerald + slate 만을 최대한 사용했다. 그렇게 해서 필요한 컴포넌트는 다음과 같다. Outlined Input시간을 투자한 부분이다. 기본 html의 input은 굉장히 단순하기에 커스텀이 필요했다.focus 했을 때, label 을 input 박스 위로 가게 하고, 부드러운 애니메이션을 넣고자 했다.Contained Button + Text Button배경을 채운 Contained Button오직 text 만 존재하는 Text ButtonTable기본 table에 css만 추가했다.Toast 메시지 success : 초록색 체크 아이콘추가, 수정, 삭제가 성공적으로 완료되었음을 표시한다. warn : 노란색 경고 아이콘입력값의 유효성검사가 일치하지 않을때 표시한다.  ✅ 3. 결과물 ✅ 4. 고민했던 부분 지출 항목이 문자여야하는데, 이를 어떻게 판별하는가? 이다.애매한 부분이 지출 항목이 "맥북 pro 16" 처럼 오직 문자 type으로 이뤄지지 않고, 여러 type이 같이 존재할 수 있다. 그래서 이걸 어떻게 예외처리 해줄지 고민했었는데, 일단은 지출 항목을 절대 숫자로만 이뤄지지는 않을 것 같아서, isNaN()만을 판별했다. 이 부분은 고민이 더 필요할 것 같다.. toast 메시지의 색상을 어떻게 할 것인가? 이다.상품을 "삭제" 했을 경우, toast 메시지의 아이콘 색상을 고민했었다.빨간색 -> 부정적 의미 -> 삭제와 연관된다! -> 그러나 error 와도 연관됨 -> 사용자 입장에서 "에러가 났다..!" 로 혼동할 수 있음.초록색 -> 삭제가 완료되었다! 에 의미를 둠-> 그러나, 추가, 수정, 삭제도 어쨌든 완료이기에, 다른 기능임에도 아이콘 색상이 같아서 구별이 어렵다는 문제가 존재함.고민 끝에 error와 연관되는 빨간색보다는 초록색으로 가되, "추가", "수정", "삭제" text 를 크고 두껍게 처리해주는 것으로 합의를 보았다!후기UI 라이브러리만 사용하다가, tailwind css로 직접 컴포넌트를 만드니까, 생각보다 시간이 오래 걸렸다. 중간에 계속 애니메이션도 적용된 라이브러리를 사용하고 싶었지만, 이를 css로(비록 tailwind css지만) 직접 완성시켰을때 상당히 뿌듯했다!또한, 기능목록을 제대로 작성한건지 모르겠다. 조금 더 깔끔하게 작성하고 싶은데, 어디까지 자세하게 적어야하는지, ui 부분도 자세하게 적어야하는지(버튼 색깔이 바뀐다거나)를 잘 모르겠다. 더 공부해봐야겠다!  

웹 개발프론트FE워밍업클럽

Dream

[ 인프런 워밍업 클럽 Study FE 0기 ] Week 2 발자국

발자국워밍업 스터디 클럽이 2주 차에 접어들었습니다. 해당 발자국은 따라 하며 배우는 리액트 A-Z (섹션 0-5) 중심으로 작성되었습니다.요약React를 사용하려면 Node.js가 필요하다. Node.js를 설치하면 NPM도 같이 설치되니 꼭 Node.js를 설치하자. Node.js 공식 홈페이지에 접속하면 2개의 Node 버전이 있는데, 그중에서 안정적인 버전인 LTS를 설치하면 된다. Section 01. React[ React란? ]리액트는 사용자 인터페이스(user interface, UI)를 만들기 위해서 사용되는 자바스크립트의 라이브러리다. 리액트는 인터렉션이 많은 웹 앱을 개발하기 위해서 주로 사용된다. 이렇게 사용자 인터페이스를 만들기 위해 도움을 주는 TOOL로는 리액트 말고도 Vue.js와 Angular.js가 있다.React: 라이브러리Angular, Vue: 프레임워크[ Framework vs Library ]프레임워크와 라이브러리를 대략 설명하자면 다음과 같다.Framework : 어떠한 앱을 만들기 위해 필요한 대부분의 것을 가지고 있다.Library : 어떠한 특정 기능을 모듈화 해 놓은 것이다.프레임워크는 앱을 만드는데 필요한 대부분의 라이브러리를 가지고 있으며, 라이브러리들은 특정 기능을 위해 모듈화 되어있다.리액트는 라이브러리이다. 왜냐? 리액트는 전적으로 UI를 렌더링 하는 데 관여하기 때문이다. 리액트는 여러 모듈을 사용하며 앱을 관리한다.라우팅: react-router-dom …상태관리: redux, mobx …빌드: webpack, npm …테스팅: Eslint, Mocha …[ React Component ]리액트를 공부하다보면 무조건 마주치는 단어가 있다. 바로 컴포넌트이다. 리액트는 컴포넌트 기반이라고 하는데, 이 컴포넌트는 무엇을 말하는 것일까?컴포넌트(Component): React로 만들어진 웹/앱을 이루는 최소한의 단위리액트는 이 컴포넌트를 통해서 웹/앱을 개발하게 된다.리액트는 여러 컴포넌트 조각으로 되어있다. 이것은 블록같다고 생각하면 된다. 여러 블록 조각을 맞추고 쌓아 올려 하나의 블록 작품을 완성하는 것. 리액트도 마찬가지로 컴포넌트를 이리저리 조합하고 쌓아올려 하나의 웹 페이지를 구성하게 된다.리액트 컴포넌트에는 2가지가 있다.클래스형 컴포넌트함수형 컴포넌트React는 여러 컴포넌트 조각으로 구성된다.개인적인 설명을 덧 붙이자면 리액트는 레고 블럭과 같다고 생각한다. 레고 블럭들을 하나 둘 씩 쌓아 올려 하나의 완성된 레고 작품을 만드는 것이다.[ Component 종류 ]React는 2개의 컴포넌트 종류가 있다.클래스형 컴포넌트(Class Components)class App extends Component { render() { return <h1>Hello, ReactJS!</h1>; } }함수형 컴포넌트(Functioanl Components)function App() { return <h1>Hello, ReactJS!</h1>; }💡 현재 함수형 컴포넌트를 HOOK이랑 해서 많이 사용한다.💡 참고로 컴포넌트를 작성할 때 반드시 대문자 시작을 해야 한다. 소문자 시작 시 body, h1, p 같은 DOM 태그로 인식해 버린다.[ 브라우저가 그려지는 원리와 가상 돔 ]React의 가장 큰 특징은 가상 돔(Virtual DOM)이다. 이것을 사용하는 이유는 인터렉션 때문이다. 이 인터렉션에 의해 DOM에 변화가 발생하면 다시 DOM을 재구성하기 시작한다.JS 발자국에도 남겼었지만 웹 브라우저의 경우 다음과 같은 과정을 겪고 이 과정은 비용이 꽤 든다.Critical Render Path (웹 페이지 렌더링 과정): 데이터 파싱(HTML) ➔ DOM Tree 생성 ➔ CSSOM Tree 생성 ➔ JS 실행 ➔ Render Tree 생성 ➔ Layout 생성 ➔ PaintDOM을 재구성 한다는 것은 위 렌더링 과정을 다시 반복한다는 것이다. 즉, 인터렉션이 일어날때마다 위 과정을 다시 한다. 이것을 보완하기 위해서 나온 것이 가상 돔이다.가상돔 과정을 살펴보자..!데이터가 바뀌면 가상 돔에 렌더링 되고, 이전에 생긴 가상 돔과 비교를 해서 바뀐 부분만 실제 돔에 적용 시킨다.바뀐 부분을 찾는 과정을 Diffing이라고 부른다.바뀐 부분만 실제 돔에 적용 시키는 것을 Reconciliation(재 조정)이라고 부른다.[ Create React App 을 이용해서 리액트 설치하기 ]create-react-app 을 통해서 원하는 위치에 리액트를 설치할 수 있다. 이 때, Webpack과 Babel이 함께 설치가 된다. 따라서 React 앱 생성 전에 Webpack과 Babel에 대해서 간단히 알고 가자.Webpack정의: Webpack: 웹팩은 오픈 소스 자바스크립트 모듈 번들러써 여러 개로 나누어져 있는 파일들을 하나의 자바스크립트 코드로 압축고 최적화하는 라이브러리이다.장점여러 파일의 자바스크립트 코드를 압축하여 최적화할 수 있기 때문에 로딩 줄일 수 있. (네트워크 비용 줄음)모듈 단위로 개발이 가능하여 가독성과 유지 보수가 쉽다.Babel정의: 최신 자바스크립트 문법을 지원하지 않는 브라우저들을 위해서 최신 자바스크립트 문법을 구형 브라우저에서도 돌 수 있도록 변환 시켜주는 라이브러리이다.⇒ 이러한 Webpack과 Babel은 개발자가 React 개발 시 알아서 설정 해야 하지만 Create-React-App을 사용해서 React 앱을 생성하면 Babel이나 Webpack 설정이 이미 되어있기 때문에 준비 시간이 단축된다.[ Create-React-App ]프로젝트를 진행할 폴더 생성VSC에서 해당 프로젝트 폴더 열기Termial에 npx create-react-app 생성할파일명 입력강의에서는 npx create-react-app ./을 입력하였다../는 현재 위치를 뜻한다.[ npx create-react-app 에 대하여 ]npx: 노드 패키지 실행을 도와주는 도구이다.npx create-react-app이란 npm 레지스트리에 잇는 패키지를 ./에 실행시켜서 React를 설치해 주는 것이다.실행하는 방법실행하고자 하는 리액트 파일 위치에서 npm run start 입력(강의에서는 npm run start방법만 소개시켜 주셧는데 npm start도 가능하다.)🤔 개인적으로 요즘 vite에 관한 이야기가 보이는데 이도 조사해 보아야겠다… Section 2. 간단한 To-Do 앱 만들며 리액트 익히기[ create react app ]create-react-app으로 리액트를 설치하면 여러 파일이 등장하는데, 이 중에서 절대로 이름을 수정해서는 안되는 파일이 존재한다.public/index.html: 페이지 템플릿src/index.js: 자바스크립트 시작 점조심하자!💡그리고 우리가 새로 js, jsx, css 등 직접 생성할 파일들은 src 폴더에서 하면 된다. Webpack이 src/ 부분에만 작동하기 때문이라고 한다.[ package.json ]해당 프로젝트에 대한 정보들이 들어있다. 프로젝트 이름, 버전, 필요한 라이브러리와 라이브러리들의 버전이 명시되어 잇다.[ 싱글 페이지 애플리케이션(single-page application, SPA) ]싱글 페이지 애플리케이션(single-page application, SPA)은 서버로부터 완전한 새로운 페이지를 불러오지 않고 현재의 페이지를 동적으로 다시 작성함으로써 사용자와 소통하는 웹 애플리케이션이나 웹사이트를 말한다.위키백과React.js는 SPA이다. 즉, 어떠한 데이터에 관한 교체 이벤트가 발생했을 때, 서버로부터 페이지를 새롭게(html 파) 받아와 구성하는 것이 아니라 content를 바꿔치기 한다. 이는 HTML 5의 History API를사용해서 가능하게 만든다.[ History API ]전통적인 웹 사이트는 a page에서 b page로 이동할 때 a.html을 보여주다가 b.html을 보여주면 되었지만 SPA의 경우 오직 1개의 HTML(index.html)이 존재한다. 따라서 페이징 전환을 하기 위해서 HTML 5 History API를 이용한다.History.back(): 세션 기록의 바로 뒤 페이지로 이동하는 비동기 메서드History.forward(): 세션 기록의 바로 앞 페이지로 이동하는 비동기 메서드History.go(): 특정한 세션 기록으로 이동하게 해 주는 비동기 메서드History.pushState(): 주어진 데이터를 세션 기록 스택에 넣어준다.History.replaceState(): 최근 세션 기록 스택의 내용을 주어진 데이터로 교체한다.생성했던 React 프로젝트에서 public/index.js를 살펴보면 <div id="rood"></div>가 있다.그리고 src/index.js 코드에는 document.getElementById('root')라는 코드가 있다.자바스크립트 파일의 시작 점인 src/index.js에서 id값이 rood인 요소를 찾아 그곳에 해당 요소들을 렌더링하는 것이다. 즉, div라는 최상위 루트 노드 아래에 직접 정의한 요소를 더해 화면을 꾸며나가는 것이다!![ JSX ]JSX는 Javascript Syntax Extension의 약자로 자바스크립트의 확장 문법이다.리액트에서는 이 JSX를 이용해서 화면에서 UI가 보이는 모습을 나타내준다.JSX 사용이 필수는 아니나 사용하면 가독성이 너무 좋아서 필수 아닌 필수이다. (애초에 리액트 개발자들 대부분이 JSX를 사용한다고 한다.)JSX는 createElement를 쉽게 사용하기 위해서 사용한다.모든 UI를 만들때 마다 createElement를 사용해서 컴포넌트를 만들 수 없다.Ract는 React.crateElemnt API를 사용해서 엘리먼트를 생성한 후에 이 엘리먼트를 In-Memory에 저장한다. 그리고 RaectDOM.render 함수를 통해 웹 브라우저에 그린다.JSX를 사용하면 Babel이 사용한 문법을 crateElemnt로 자동 변환해준다. 따라서 그냥 개발자는 자유롭게 JSX 사용하면 된다.단, JSX는 컴포넌트에 여러 요소가 있다면 반드시 부모 요소 하나로 감싸줘야 한다.// 안된다. // 자식 요소가 여러 개 라면 부모 요소로 감싸줘라. function hello() { return ( <div>Hello, Raect!</div> <div>Hello, Wrold!</div> ); } // 이렇게 말이다. function hello() { return <div> <div>Hello, Raect!</div> <div>Hello, Wrold!</div> </div>; }💡 만약 JSX에서 JS 코드를 사용하고 싶다면 { } 내부에 작성해주면 된다.[ React와 Key ]map()을 사용한다면 언제나 명심해야 하는 것. KEY. 이것을 넣지 않는다면 에러가 발생한다.React에서 요소의 리스트를 나열할 때는 Key를 넣어줘야 한다. Key는 React가 변경, 추가 또는 제거된 항목을 식별하는 데 도움이 된다.추가적으로, 이 Key에 지정하는 값은 순회하고자 하는 목록의 아이템에 대한 ID 값이면 된다. 즉, 고유한 값이여야 한다. 정 없으면 index 넣어도 되지만 index 값은 권장하지 않는다.리액트는 가상 돔을 이용해서 바뀐 부분만 실제 돔에 적용한다. 그렇다면 리스트를 나열할 때 바뀐 부분만 어떻게 찾을까? 바로 이 key를 이용해서 어떠한 부분이 바뀌었는 인식하는 것이다.[ state ]정말 정말 중요한 개념!!!리액트에서 데이터가 변할 때 화면을 다시 렌더링 해주기 위해서 React State를 사용한다. State란 무엇일까?간단히 말해서 변수이다.단, 이 변수의 값이 변경되면 컴포넌트들이 재렌더링 된다.state에는 리액트의 흐름에 관한 데이터와 관련이 있다. Section 3. To-Do 앱 최적화 하기[ React HOOK ]엄청나게 중요하다. 이 HOOK은 클래스형 컴포넌트처럼 함수형 컴포넌트에서도 state와 생명주기 메서드를 사용할 수 있도록 해주는 메서드이다.클래스 형 컴포넌트에서는 Mounting, Updating, Unmounting 3단계 따라서 생명주기 메서드를 제공한다.Mounting: componentDidMount()Updating: componentDidUpdateUnmounting: componentWillUnmount()함수형 컴포넌트에서는 이를 위해 HOOK을 사용한다.[ HOC(Higher Order Component) ]화면에서 재사용 가능한 로직만을 분리해서 component로 만들고, 재사용 불가능한 UI와 같은 다른 부분들은 parameter로 받아서 처리하는 방법이다.HOC는 HOOK이 나오기 전에 사용했던 부분이다.Wrapper가 많아지면 흐름 파악이 어려워서 이제 잘 안 쓴다.HOC를 만들고 싶으면 Custom HOOK을 사용하자.[ HOOK ]기본적으로 알고 있어야 할 HOOK은 다음과 같다.useState()리액트의 유동적인 데이터들은 state에 담아 사용하기 위해 이용하는 HOOK클래스형 컴포넌트의 setState와 같이 state 객체에 대한 업데이트 실행단!!! state 변경 시 재 렌더링이 일어남useEffect()사이드 이팩트 처리 HOOK클래스형 컴포넌트의 생명 주기 함수 역할 수행useMemo()최적화 용 HOOK, 의존성 배열에 따라 작동의존성 배열에 있는 값이 변하면 지정한 함수를 실행하여 해당 반환 값을 반환useCallback()최적화 용 HOOK, 의존성 배열에 따라 작동의존성 배열에 있는 값이 변하면 함수를 반환useRef()요소의 참조를 위해 사용하는 HOOK[ Props ]Props는 Properties의 줄임말상위 컴포넌트에서 하위 컴포넌트로 데이터를 전송하고 싶을 경우 사용읽기 전용으로 자녀 컴포넌트에서 강제로 이 값을 변경할 수 없다.전달 받은 props가 state고 이 값을 바꾸고 싶다면 props로 set함수를 넘기고 이것을 이[ TailWindCSS ]HTML 안에서 CSS 스타일을 할 수 있게 해주는 CSS 프레임워크빠른 스타일 작업 가능id 혹은 class 명을 작성하기 위해 머리를 혹사 시키지 않아도 된다.class에 특정 키워드를 넣어서 CSS 조작정해진 속성 키워드가 워낙 많으니 공식 홈페이지 검색 필수다[ 리액트 불변성 ]불변성을 지키며 개발을 하자!참조 타입에서 객체나 배열의 값이 변할 때 원본 데이터가 변경되면 예상치 못한 오류가 발생할 수 있다.불변성을 지킬 수 있는 참조 관련 메서드:spread operator, map, filter, slice, reduce불변성을 해치는 참조 관련 메서드:splice, push[ React.memo ]React.memo는 Higher-Order Components(HOC)이다. 불필요한 컴포넌트 렌더링을 방지할 수 있게해준다. (일종의 최적화 용 HOC) Section 4-5. Netflix 앱 만들기주로 실습 내용 이었다. 정리할 이론만 추려내 보겠다.[ Styled Component ]자바스크립트 파일 안에서 CSS를 처리할 수 있게 해주는 라이브러리[ React Router Dom ]React Router DOM을 이용하면 웹/앱에서 동적 라우팅을 구현할 수 있다. 라우팅이 실행 중인 앱 외부의 구성에서 처리되는 기존 라우팅 아키텍처와 달리 React Router DOM은 앱 및 플랫폼의 요구 사항에 따라 컴포넌트 기반 라우팅을 용이하게 한다.React Router DOM을 사용하기 위해서는 몇 가지 설정을 해야한다.index.js에서 BrowerRouter로 루트 컴폰너트를 감싸준다.BrowserRouter은 HTML 5 History API를 사용하여 UI를 URL과 동기화 된 상태로 유지해준다.import { BrowserRouter } from 'react-router-dom'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter > <App /> </BrowserRouter> </React.StrictMode> ); 여러 컴포넌트 생성 및 라우트를 정의한다.Routes와 Route를 사용한다.Routes: 앱에서 생성될 모든 개별 경로에 대한 컨테이너 상위 역할을 한다.Route: 단일 경로를 만드는 데 사용된다.path 속성: 원하는 컴포넌트의 URL 경로를 지정한다.element 속성: 경로에 맞게 렌더링 되어야 하는 컴포넌트를 지정한다.import { Routes, Route } from "react-router-dom"; function App() { return ( <div className="app"> <Routes> <Route path="/" element={<Home />}> <Route path="about" element={<About />} /> <Route path="contact" element={<Contact />} /> </Route> </Routes> </div> ); } +) <Link />를 통해 경로 이동하기Link 구성 요소는 HTML의 a 태그와 유사하다.to 속성은 링크가 유저를 데려가는 경로를 지정한다.앱 구성 요소에 나열된 경로 이름을 생성했기 때문에 링크를 클릭하면 경로를 살펴보고 해당 경로 이름으로 구성 요소를 렌더링한다.import { Link } from "react-router-dom"; function Home() { return ( <div> <h1>홈페이지</h1> <Link to="about">About 페이지를 보여주기</Link> <Link to="contact">Contact 페이지를 보여주기</Link> </div> ); } [ 중첩 라우팅 ]라우팅은 중첩 처리가 가능하다.[ Outlet ]자식 경로 요소를 렌더링하려면 부모 경로 요소에서 Outlet를 사용해야한다.하위 경로가 렌더링될 때 중첩된 UI가 표시될 수 있다.부모 라우트가 정확히 일치하면 자식 인덱스 라우트를 덴더링하거나 인덱스 라우트가 없으면 아무것도 렌더링하지 않는다.[ useNavigate ]경로를 바꿔준다.naviate(”/home”) ⇒ localhost:3000[ useParams ]:style 문법을 path 경로에 사용했다면 useParams()로 읽을 수 있다.function test() { return ( <Routes> <Route path="invoices/:invoiceId" element={<Invoice />} /> </Routes> ); } function Invoice() { let params = useParams(); return <h1>Invoice {params.invoiceId}</h1>; } [ useLocation ]현재 위치의 객체를 반환현재 위치가 변경될 때마다 일부 side effect를 수행하려는 경우 유용하다.[ useRoutes ]<Routes>와 기능적으로 동일하나 <Route> 요소 대신 자바스크립트 객체를 사용하여 경로를 정의한다.일반 <Route> 요소와 동일한 속성을 갖지만 JSX가 필요하지 않는다.[ Custom HOOK ]개발자가 정의하는 HOOK이다.HOOK의 이름은 use로 시작해야 한다.참고로 HOOK은 함수형 컴포넌트 또는 커스텀 HOOK에서만 호출이 가능하다.따라서 커스텀 HOOK도, 함수용 컴포넌트 또는 HOOK 내부에서 호출되어야 한다.강의에서는 useDebounce과 useOnClickOutside HOOK을 만들었다.useDebounce: input 요소에서 데이터 입력이 발생하면 설정한 set함수 때문에 매번 state 값이 바뀌고 재 렌더링이 일어난다. 따라서 keyup 이벤트의 처리를 지연시키는 커스텀 HOOK이다. (코드는 강의를 참고하자!)useOnClickOutside HOOK: 모달 창 밖의 부분을 클릭하면 해당 모달 창이 꺼지는 기능을 수행하는 HOOK이다. (코드는 강의를 참고하자!)이런 식으로 HOOK을 만들고 활용하는구나 싶었다…미션과제 총 합본 https://www.inflearn.com/blogs/7021 JS 미션 03. 퀴즈 앱[ 구현 해야하는 기능 ]1. 퀴즈 문제, 문제에 해당 하는 선택지 (선택지의 갯수가 매번 다름)2. 답 선택 시, 정답 여부에 따라 배경의 색상이 변경되어야 함문제는 data.json을 직접 작성하여 동적 생성했습니다. JS 복습 겸으로 해당 주제로 퀴즈 앱을 간단하게 만들어 봤습니다. 미션을 진행하며 문제는 없었습니다. JS 미션 04. 책 나열 앱[ 구현 해야하는 기능 ]1. 책 이름 입력 란2. 책 저자 입력 란3. 제출 버튼을 누르면 입력한 정보를 저장 함3-1. 제출 시 제출 했다는 안내 문구 떠야 함4. 제출된 데이터는 책 리스트에 출력 됨아이템은 다음과 같은 기능을 가져야 함5-1. 표기 할 데이터: 책 이름, 저자5-2. 각 아이템에는 삭제 기능이 있어야 함 구현하는데 문제가 없었습니다. REACT 01. 예산 계산기[ 구현 해야하는 기능 ]1. 지출 항목 입력 란2. 지출 비용 입력 란3. 제출 버튼을 누르면 입력한 정보를 저장 함3-1. 제출 시 제출 했다는 안내 문구 떠야 함아이템은 다음과 같은 기능을 가져야 함5-1. 표기 할 데이터: 지출 항목, 지출 비용5-2. 각 아이템에는 수정 및 삭제 기능이 있어야 함수정 버튼 클릭 시 수정 모드로 변경전체 삭제 기능이 있어야 함정말 막힘 없이 진행되다 딱 한 군데에서 문제를 맞았습니다. 상황에 맞게 알림을 띄우는 기능이었는데, JS에서는 아무런 문제 없이 해결했던 이 기능을 React에서 구현 하려고 하니 이상한 문제가 발생하더군요. 여러 동작을 해서 메시지가 많이 발생할 경우, 메시지가 예시처럼 모두 생성되는 것이 아니라 같은 자리에서 텍스트만 바뀌어서 출력이 되었습니다. 물론 잘 해결해서 과제를 마쳤습니다.회고워밍업 스터디의 2주 차에 진입하며 자바스크립트 공부를 마치고 새롭게 React 공부를 진행하며 React의 다양한 기술을 접하게 되었습니다. 특히 state, props, hook, 그리고 라우팅 부분은 처음에는 이해하기가 어려웠습니다. 그러나 부족한 이해를 보완하기 위해서 강의 내용을 정리하고, 추가적인 학습 자료를 찾아가며 개념을 확실히 파악하려고 노력했습니다.React 학습을 마치고 시작한 미션도 초반에는 막막함을 느꼈지만 코드를 작성해 나가며 수업 때 배운 내용을 적용해 가며 문제를 해결해 나갔습니다.워밍업 스터디도 이제 끝을 향해 가네요. 마무리되는 날까지 열심히 학습에 참여하고 미션 해결을 위해 도전해 보겠습니다. 

프론트엔드워밍업클럽FE

이양구

[인프런 워밍업 클럽 FE 0기] 미션8 - 디즈니 플러스 앱

🎞 Disney Plus APP GitHub 🎞 Disney Plus APP DemoRecord by ScreenToGif  개요인프런 워밍업 클럽 FE 0기의 여덟 번째 미션인 '디즈니 플러스 앱' 입니다. 따라하며 배우는 리액트 섹션 4~5(리액트로 Netflix 앱 만들기) 목표swiper 라이브러리 커스텀해보기react-oauth/google 로 구글 로그인 연동해보기 구현swiper 라이브러리 커스텀해보기// LoginPage import "swiper/css/effect-fade"; <Swiper modules={[Autoplay, EffectFade, Pagination, A11y]} autoplay={auto} effect={"fade"} pagination={{ clickable: true, }} loop={true} fadeEffect={{ crossFade: true }} slidesPerView={1} speed={2000} > {...} </Swiper> // Row.tsx import "swiper/css/mousewheel"; <Swiper modules={[Navigation, Pagination, Scrollbar, A11y, Mousewheel]} navigation pagination={{ clickable: true }} mousewheel speed={1000} spaceBetween={10} > {...} </Swiper> 2024년 3월 10일의 디즈니 플러스 메인 페이지를 그대로 옮겨보고자 swiper 라이브러리를 커스텀해봤다.로그인 페이지에서는 좌우로 넘기는 슬라이드가 아닌 fade-in-out의 슬라이드를 구현하기 위해 swiper에 EffectFade 모듈을 추가하고 fadeEffect 속성을 추가했다.이 fadeEffect가 제대로 작동하기 위해선 반드시 해당 이펙트의 css를 추가해야 한다.다른 모듈이나 컴포넌트를 추가할 때처럼 자동으로 추가되지 않으니 주의해야 한다. (이걸 몰라서 한참을 찾았다. 😥)Row 컴포넌트는 마우스 휠에 따라 움직이는 슬라이드를 만들기 위해 Mousewheel 모듈과 속성을 이용했다.이렇게 슬라이드 속성을 정한 뒤에 swiper가 렌더링하는 요소의 class를 찾아 CSS에서 원하는 디자인으로 변경하면 된다.이때 라이브러리의 CSS와 겹치는 속성이 있을 수 있기 떄문에 '!important'를 붙이는 게 좋다. react-oauth/google 로 구글 로그인 연동해보기// index.js <GoogleOAuthProvider clientId={process.env.REACT_APP_CLIENT_ID}> <BrowserRouter> <App /> </BrowserRouter> </GoogleOAuthProvider> // App.jsx const navigate = useNavigate(); const [isLogin, setIsLogin] = useState( localStorage.getItem("user") ? true : false ); useEffect(() => { isLogin ? navigate("/") : navigate("/login"); }, [isLogin]); <Routes> {isLogin ? ( <Route path="/" element={<Layout setIsLogin={setIsLogin} />}> <Route index element={<MainPage />} /> <Route path=":movieId" element={<DetailPage />} /> <Route path="search" element={<SearchPage />} /> </Route> ) : ( <Route path="login" element={<LoginPage setIsLogin={setIsLogin} />} /> )} </Routes> react-oauth/google는 구글 로그인을 지원하는 라이브러리로, 사전에 구글의 Cloud에서 API 등록을 하고 Client ID를 발급받아야 사용할 수 있다.먼저 프로젝트의 최상위에 GoogleOAuthProvider로 감싸준다.그리고 사용자의 로그인 여부에 따라 페이지를 이동시키기 위해 라우터를 설정한 App 컴포넌트에서 관련 코드를 작성했다.페이지가 렌더링 될 때 로컬 스토리지에 저장된 유저 정보를 받아오고 만약 없다면 로그인 페이지로 보내도록 했다. // loginPage const googleLogin = async (credentialResponse) => { localStorage.setItem( "user", JSON.stringify(jwtDecode(credentialResponse.credential)) ); setIsLogin(true); }; <GoogleLogin onSuccess={(credentialResponse) => googleLogin(credentialResponse)} /> GoogleLogin 컴포넌트는 react-oauth/google 라이브러리에서 지원하는 버튼 컴포넌트로 디자인 및 로그인 관련 함수가 내장되어 있다.onSuccess는 사용자의 로그인이 성공했을 때 실행되는 콜백 함수이며, 인자로 로그인한 유저의 정보를 담은 데이터를 갖는다.여기서 credential이라는 값은 유저의 정보를 담고 있는 토큰으로 암호화되어 있기 때문에 jwt-decode 라이브러리를 이용해 디코딩하여 사용해야 한다.여기서 받은 picture는 사용자의 프로필 이미지 링크를 포함하고 있어서 Nav 컴포넌트에서 사용해 로그인한 유저의 프로필 이미지로 변경했다. 회고'Netflix 앱 만들기'를 하면서 사용했던 기술이 대부분이라 오래 걸리지 않을 것 같았지만...라이브러리 알아보고 문서 읽고 실행해보고... 하는 데 너무 오래 걸린 것 같다.배너 하단의 카테고리 부분은 이전에 같은 과제를 하셨던 분의 깃허브를 참고했다. (https://github.com/kimneighbor/clone-disney-plus-app)로그인 페이지는 따라하기 싫어서 현재 디즈니 플러스 홈페이지를 보고 참고했다.그대로 하면 얼마 안 걸릴 거라 생각했는데 생각보다 라이브러리 커스텀에서 좀 애를 먹었다. 😅with_networks: "2739" 2739는 TMDB에서 디즈니 플러스 방송사(networks) 코드라서 axios의 instance 기본 값에 추가했다.몇몇 요청은 해당 파라미터가 통하지 않거나 오류를 보내기도 해서 완벽하진 않다.디즈니 플러스에서 API를 제공했다면 더 알맞게 페이지를 구현할 수 있었을 텐데 하는 아쉬움이 남는다.한편 영화 정보 API를 제공해주는 TMDB(The Movie Database) 같은 곳이 있어 감사하고 다행이라는 생각이 들었다.프론트엔드 공부하는데 API를 제공해주는 곳이 아예 없었다면 혹은 매번 일정 비용을 지불해야 했다면 얼마나 힘들었을까로그인도 사실 좀 더 좋은 라우팅 구조나 상태 관리 라이브러리를 공부하고 사용해보고 싶었지만...계속 욕심만 커지는 것 같아 최대한 간단하게 구현하려 했다.(사실 과제 밀려서 조바심에 아무것도 못 했다... 😂) 

프론트엔드워밍업워밍업클럽프론트엔드프론트FE미션과제발자국

Dream

[ 인프런 워밍업 클럽 Study FE 0기 ] Week 3 발자국

발자국3주 동안 진행된 스터디 클럽이 마무리되었습니다. 이번 스터디에서는 Next와 타입스크립트에 대해서 기초를 다지는 한 주를 보냈습니다. 이제 인프런 워밍업 클럽 스터디의 마지막 발자국을 남깁니다!요약Section 06-07. TDD 기본 및 간단한 앱 생성 및 배포대부분이 실습 위주였기에 코드가 궁금하다면 강의를 보자. 필요한 이론 개념만 요약하겠다.[ TDD(Test Driven Development) 란? ]TDD는 Test Driven Development의 약자로 테스트 주도 개발이라는 의미를 가지고 있다. 강의에서는 다음과 같이 설명한다.“실제 코드를 작성하기 전에 테스트 코드를 먼저 작성하는 것”테스트 코드를 작성한 후 그 테스트 코드를 Pass 할 수 있는 실제 코드를 작성한다.원하고자 하는 기능의 테스트 코드 작성 ⇒ 테스트 실행 FAIL ⇒ 테스트 코드에 맞는 실제 코드 작성 ⇒ 테스트 실행 PASSTDD는 테스트 코드를 작성한 뒤에 실제 코드를 작성한다. 단, 설계 단계에서 프로그래밍의 목적, 테스트 케이스를 작성해야 한다.요구 사항 접수요구 사항 분석 및 설계 ⇒ 목적 및 테스트 케이스 결정테스트 코드 작성FAIL (오류, 수정)이 난 코드를 테스트 케이스에 추가 후 이를 바탕으로 재 설계테스트가 통과 (PASS)된 코드만 개발 단계에서 실제 코드로 작성⇒ 반복적으로 코드의 테스트를 진행함으로서 오류 개발을 낮추고 소스 코드를 깔끔히 관리하는 것. 따라서 다음과 같은 장점이 있다.디버깅 시간 단축재 설계 시간 단축오류 발생 확률 저하추가 구현 용이테스트 기간 단축이렇게 보면 엄청나게 좋아 보이지만 모든 개발자들이 TDD 방식을 사용하지는 않는다. 아래 예시에서 첫 번째가 큰 듯 하다.익숙한 기존 방식을 버리지 못함생산성 저하 [ React Testing Library ] React Testing Library는 사용자가 컴포넌트를 사용하는 것처럼 테스팅하는 React의 테스트 라이브러리이다.React Testing Library는 React 구성 요소 작업을 위한 API를 추가하여 DOM Testing Library 위에 구축된다.DOM Testing Library란 DOM 노드를 테스트하기 위한 매우 가벼운 솔루션이다.Create React App으로 생성된 프로젝트는 React Testing Library를 지원하기 때문에 따로 설치할 필요가 없다.행위 주도 테스트(Behavior Driven Test)이다.EX) 사용자가 어떠한 행위로 이벤트가 발생되었을 때 프로그램이 어떻게 반응하는지~  [ Jest ]Jest는 현: Meta / 전: Facebook에서 만든 테스팅 프레임워크이다. 최소한의 설정으로 동작하며 Test case를 만들어서 어플리케이션 코드가 잘 돌아가는지 확인해준다Jest는 테스트 실행 환경을 제공한다.DOM이 없다(참고로 DOM 없이 React 테스트 X) ⇒ 따라서 React Testing Library와 함께 사Jest를 사용하려면 설치를 해야한다.라이브러리 설치: npm install jest —save-devTest 스크립트 변경: “test” : “jest” OR “jest —watch All”테스트를 작성할 폴더 및 파일 기본 구조 생성 Section 08. Next.js와 TypeScript[ NextJS ]Next.js란 SSR(Server-Side-Rendering)을 쉽게 구현할 수 있게 도와 주는 React 프레임워크이다.일반적으로 리액트는 SPA(Single-Page Application)를 이용해서 CSR(Client-Side-Rendering)을 하기 때문에 좋은 점도 많지만, 검색엔진 최적화(SEO)에 관한 단점이 있다. [ CSR과 SSR ]💡 사전 지식: 검색 엔진에 도움을 주는 것은 HTML의 시맨틱 태그들이다! 1. CSR (Client-Side-Rendering)React에서는 CSR 방식 기본적으로 사용CSR 초기 접속 시 렌더링 동작 방식:클라이언트가 서버에 페이지 내놔 요청서버는 빈 페이지를 클라이언트에게 전달 ⇒ 실제로 개발자 모드로 가면 HTML에 뼈대만 있는 것을 볼 수 있다.클라이언트는 JS 파일을 보고 렌더링 ⇒ 즉, 빈 페이지를 클라이언트에서 처리한다. ⇒ 서버에 대한 의존도가 별로 없다.결론: 검색 엔진에 영향을 주는 HTML이 빈 페이지니까 검색 엔진에 노출될 일이 거의 없다. [ SSR ]Next.js에서 사용하는 방식React에서도 이 방식을 사용할 수 있으나 구현이 어렵기 때문에 React로 굳이?라는 느낌.SSR초기 접속 시 렌더링 동작 방식:클라이언트가 서버에 페이지 내놔 요청서버는 미리 구성된 정적 파일을 클라이언트에게 전달. (정적 파일: HTML, CSS …)클라이언트는 전달 받은 스크립트를 실행하여 화면을 브라우저에 그림결론: 빈 페이지가 아닌 화면을 보여주기 때문에 SEO에 장점이 있다.Next.js 설치 방법 (필자가 npx만 사용해서 npx만 기록)npx create-next-app@latestnpx create-next-app@latest —typescript  [ create-next-app 기본 구조 ]Pages이 폴더 안에 페이지들을 생성만약 about이라는 페이지를 만드려면 pages폴더 안에 about.tsx를 생성해주면 된다.index.tsx가 처음 “/” 페이지로 지정된다._app.tsx는 공통되는 레이아웃을 작성한다.모든 페이지에 공통으로 들어가는 걸 넣어주려면 여기에 넣어주면 된다.url을 통해 특정 페이지에 진입하기 전 통과하는 인터셉터 페이지다. public이미지 같은 정적(static) 에셋들을 보관         styles(강의에서는 있는데 내 폴더에는 없다..) 스타일링을 처리해주는 폴더 모듈(module) css는 컴포넌트 종속적으로 스타일링하기 위한 것이며, 확장자 앞에 module을 붙여줘야한다. next.config.js Next.js는 웹 팩을 기본 번들러로 사용한다. 그래서 웹 팩에 관한 설정들을 이 파일에서 해줄 수 있다.  [ Pre-rendering ]서버에서 각 페이지의 HTML 파일을 미리 생성하는 것으로, 모든 페이지가 pre-render된다. 이렇게 하기 때문에 SEO 검색 엔진 최적화가 좋아진다.            [ Data Fetching ]Next.js에서는 데이터를 여러 방법으로 가져온다. 상황에 맞는 것을 알아서 잘 사용하자.보통 React에서는 데이터를 가져올 때 useEffect 안에서 처리한다. 하지만 Next.js에서는 다른 방법을 사용해서 가져온다. (물론 Next에서도 useEffect를 이용해 가져올 수도 있다.)getStaticProps: Static Generation으로 빌드할 때 데이터를 불러온다. (미리 한 번에 만들어 줌)getStaticPaths: Static Generation으로 데이터에 기반하여 pre-render시 특정한 동적 라우팅 구현getServerSideProps: Server Side Renderin으로 요청이 있을 때 데이터를 불러온다. [ TypeScript ]타입스크립트는 자바스크립트에 타입을 부여한 언어이다. 즉, 자바스크립트의 확장된 언어이다. 타입 스크립트는 자바스크립트와 달리 브라우저에서 실행하려면 파일을 한 번 변환해주어야 한다. 이 변환 과정을 컴파일(compile)이라고 한다.타입스크립트의 특징을 정리해보자.자바스크립트의 타입 확장 버전즉, 동적 언어: 자바스크립트 / 정적 언어: 타입스크립트이다.개발 환경에서 에러를 잡는 걸 도와준다.type annotations을 사용해서 코드를 분석할 수 있다.오직 개발 환경에서만 활성화 된다.타입 스크립트와 성능 향상은 관계없다.즉, 타입스크립트가 막 자바스크립트보다 성능이 뛰어나다!!!! 가 아니다. 자바스크립트는 굉장히 자유도가 높다. 하지만 이는 규모 큰 프로젝트를 진행한다면 오히려 독이 될 수 있다. (지옥의 타입 에러가 시작된다고 한다.) 뭐 이와 같은 이유로 타입스크립트 사용을 권장을 한다. 물론 필수 X타입스크립트는 자바스크립트를 단순화하여 더 쉽게 읽고 디버그할 수 있도록 한다.코드를 더 쉽게 읽고 이해할 수 있다.오픈 소스이다.정적 검사와 같은 자바스크립트 IDE 및 사례를 위한 매우 생산적이 개발 도구를 제공한다.자바스크립트보다 더 개선된 코드를 작성할 수 있다.ES6의 모든 이점과 더 많은 생산성을 제공한다.고통스러운 버그에서 구출해준다. [ TypeScript Type ]타입이란 컴파일러에게 "야! 나 이 타입 쓸거다?"라고 선전포고하는 것이다. 즉, 내가 어떠한 value를 사용할 것인지 추론이 가능하도록 표기하는 것. 이는 컴파일러에게도 좋겠지만 코드를 읽는 개발자도 편하다.Primitive/Object types의 경우 다음과 같다. (이건 너무 기초에 기초니까 정리 PASS)Primitive: string, number, boolean, null, undefined, symbol Object : function, array, classes, object 타입 스크립트에서는 기본적인 타입 및 특별한 타입을 제공한다.Any잘 알지 못하는 타입의 경우 사용서드 파티 데이터 로딩 시.. 도대체 뭔 데이터가 올지 모를 때 사용하면 된다.\물론 이런 "뭔 타입 올지 머르는데...?"와 같은 확실치 않은 것은 최대한 안 쓰는 게 좋다.Union또는 이다. OR이니까 | 사용한다.let code: string | number; 이렇게 하면 code의 값은 string 또는 number라는 뜻.Tuple배열에 타입 지정하는 버전~let ex: [number, string] = [1, "hello"];Enumenumerated type을 의미하며, 값들의 집합을 명시하고 이를 사용하도록 만든다.별도의 값을 설정하지 않으면 기본적으로 0 스타트이다.Void반환될 때 반환되는 데이터가 없을 경우 쓴다.참고로 함수들에서 return에 반환할 데이터 명시 안 해도 기본적으로 undefined가 반환 된다.이렇게 반환되는 값이 없어서 undefined가 반환되는 경우 void 쓰면 된다.Never아에 완전히 영원히 값이 반환할 일이 없는 경우.. 기본적으로 함수가 undefined를 반환하는데 그 조차도 하지 않는 경우에 쓴다.오류 리턴이나 영원히 끝나지 않을 무한 루프에서 쓴다.  [ annotation, type, interface ]annotation: 개발자가 타입을 타입스크립트에게 말해주는법const str: string = "Hello, World!"; const num: number = 1;type: 이 키워드를 사용해서 내가 타입 정의가 가능하다.type Person = { name: string, age: number }interface: type과 비슷하게 정의 가능~interface Person = { name: string, age: number }다만 interface는 부가적인 기능이 많다고 한다. (나중에 찾아봐야겠다.) [ Type assertion ]타입스크립트에서는 시스템이 추론 및 분석한 타입 내용을 우리가 원하는 대로 얼마든지 바꿀 수 있다. 이때 "타입 표명(type assertion)"이라 불리는 메커니즘이 사용된다.var foo = {}; foo.bar = 123; // 오류: 속성 bar가 존재하지 않음. foo.bas = "hello"; // 오류: 속성 bas가 존재하지 않음.컴파일러는 foo type이 빈 {}로 인식한다. 따라서 존재하지 않은 bar와 bas에 접근하려고 하는 것으로 보이기 때문에 당연히 오류를 낸다. 하지만 type assertion을 사용하면 이런 상황을 피할 수 있다.interface Foo { bar: number; bas: string; } var foo = {} as Foo; foo.bar = 123; foo.bas = "hello";저기 서 사용된 as가 type assertion이다.완전 간단하게 "컴파일러야. { } (왼쪽에 명시된 얘)는 Foo(오른쪽)와 같으니까 의심하지 말고 Foo(오른) 처럼 사용해라"라는 뜻이다. [ Next.js 13 ]12에서 13으로 넘어가면서 Next.js가 많이 바뀌었다고 한다! 위에서 설명한 것은 12니까 이 강의가 굉장히 중요해 보였다. (현재 14.1까지 나와따)다음 추가된 사항이고... 대부분 실습 위주 강의라서 간단하게 정리만 했다.App Routerpage 파일이 해당 경로의 페이지 컴포넌트 처리된다.Layout기본 레이아웃 정의 가능.공통된 레이아웃을 적도 children을 감싸는 형식으로 하면 된다.Server/Client ComponentNext에서는 기본이 Server Component이다.State, Hook, 이벤트 처리 및 브라우저 api 사용 => 이 경우 Client Component를 사용해야한다.코드 작성 전 맨 첫 줄에 "use client" 명시해주면 된다. Section 09. 리액트 Version 18새로 추가된 내용Automatic batchingSuspense on the server Transition등등...  [ Automatic batching ]배칭은 업데이트 대상이 되는 상태 값들을 하나의 그룹으로 묶어서 한 번의 리렌더링에 업데이트가 모두 진행될 수 있게 해주는 것이다. 하나의 함수 내부에서 여러 개의 state를 조작했을 경우 리렌더링이 그 state의 수 만큼 실행되는 것이 아니라 딱 1번만 최종 실행된다는 것이다. 그리고 제목에서 보이듯이 자동처리다. 개발자가 딱히 배칭을 위해 설정할 것은 없다.더 나은 성능을 위한 더 적은 리렌더링 가능이벤트 핸들러 밖에서도 작동필요할 때 제외 가능   [ Suspense on the server ]서버 사이드 렌더링서버에서 전체 앱에 대한 데이터 가져옴서버에서 전체 앱을 HTML로 렌더링하고 응답으로 보냄클라이언트에서 전체 앱에 대한 자바스크립트 코드 로드클라이언트에서 자바스크립트 논리를 전체 앱에 대해 서버 생성 HTML에 연결(hydration) [ Transition ]리액트에서 어떠한 업데이트가 Urgent하며 어떠한 것이 그러하지 않은지 알려준다. Section 10. 리덕스리덕스는 자바스크립트의 애플리케이션위한 상태 관리 라이브러리이다.CreateStore()앱의 전체 상태 트리를 보유하는 Redux 저장소를 만든다.앱에는 하나의 스토어만 있어야 한다.getState()애플리케이션의 현재 상태 트리를 반환한다.subscribe(listener)listener 함수 등록Action이 dispatch될 때마다 리스너 함수가 호출된다.그런 다음 getState()를 호출하여 콜백 내부의 현재 상태 트리를 읽을 수 있다. [ Provider ]<Provider> 구성 요소는 Redux Stroe 저장도에 액세스해야 하는 모든 중첩 구성 요소에서 Redux Store 저장소를 사용할 수 있도록 한다.React Redux 앱의 모든 Reqact 구성 요소는 저장소에 연결할 수 있으므로 대부분의 응용 프로그램은 전체 앱의 구성 요소 트리가 내부에 있는 최상위 수준에서 <Provider>를 렌더링한다.그런 다음 Hooks 및 연결 API는 React의 컨텍스트 메커니즘을 통해 제공도니 저장소 인스턴스에 액세스할 수 있다. [ useSelector, useDispatch ]리덕스 HOOKuseSelector스토어의 값을 가져올 수 있다.useDispatchstore에 있는 dispatch 함수에 접근. [ 리덕스 미들웨어 ]Redux 미들웨어는 액션을 전달하고(dispatch) 리듀서에 도달하는 순간 사이에 사전에 지정된 작업을 실행할 수 있게 해주는 중간자이다.💡 로깅, 충돌 보고, 비동기 API 통신, 라우팅 등을 위해 미들웨어를 사용한다!미들웨어 생성에 관한 것은 강의에서 실습으로 소개하니 궁금하면 강의를 보자. [ 커링 ]자바스크립트 고급 기술이다. (다른 언어에도 존재.)f(a, b, c)처럼 단일 호출로 처리하는 함수를 f(a)(b)(c)와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합될 수 있게 변환하는 것이다. [ Thunk ]리덕스를 사용하는 앱에서 비동기 작업을 할 때 많이 사용하는 방법이다. "thunk"라는 단어는 "일부 지연된 작업을 수행하는 코드 조각"을 의미하는 프로그래밍 용어이다. Redux 스토어의 dispatch 및 getState 메서드와 상호 작용할 수 있는 내부 로직이 있는 함수를 작성할 수 있다. [ Redux Toolkit ]Redux 툴킷은 Redux 로직을 작성하기 위한 공식 권장 접근 방식이다.Redux 코어를 둘러싸고 있으며 Redux 앱을 빌드하는 데 필수적이라고 생각하는 패키지와 기능이 포함되어 있다.Redux 툴킷은 Redux 작업을 단순화하고 일반적인 실수를 방지하고 Redux 애플리케이션을 더 쉽게 작성할 수 있다.Redux 툴킷에는 Redux Thunk가 기본으로 들어가있다. 사용 순서:configureStore을 사용하여 Redux Store 만들기React 컴포넌트에 Redux Stroe 제공 순서주위에 React-Redux <Provider>로 컴포넌트를 감싸준다.Redux Stroe를 <Provider store={store}>로 전달한다.creatSlice로 Redux 슬라이스 리듀서 생성React 컴포넌트에서 React-Redux useSelector/useDispatch Hook 사용미션과제 총 합본 https://www.inflearn.com/blogs/7021React 미션 02. 디즈니+ 클론과제 중에 가장 어려웠던 부분은 구글 연동이었습니다. 처음으로 구글 연동을 시도하면서 계속해서 오류가 발생했고, 한 번은 무한 로그인에 걸려 프로그램이 멈춘 적도 있었습니다. 이때는 정말 심장이 철렁거렸습니다. 그러나 마음을 비우고 처음부터 차근차근 시도하니 로그인 연동에 성공했습니다.또 다른 어려움은 마우스를 대면 비디오가 나오는 부분이었습니다. 이미지와 비디오를 겹쳐놓고 마우스가 hover될 때 비디오만 보여주는 부분인데, CSS의 position 이해가 부족한 것인지 배치가 제대로 되지 않아 계속 시도해야 했습니다. position에 대한 부분은 추가적인 학습이 필요하다고 느꼈습니다.React 미션 03. 포켓몬 도감포켓몬 과제는 난이도가 상이었고 디즈니와 다르게 참고할 코드조차 없는 어려운 과제였습니다. 이것은 정말 자기 스스로 머리를 쪼개가며 해결 해야하는 과제로 보였기에 가장 마지막에 해결을 하겠다 생각했습니다. 포켓몬 도감를 시작할 때, 이제야 웹을 공부를 하는 제가 이 과제를 과연 완성을 할 수 있을까? 했는데, 걱정과 다르게 디즈니보다 훨씬 쉬었습니다...! 코드를 작성하면서 렌더링이 안 일어나는 이상한 상황을 겪었지만(map key 값이 고유하지 못해서 발생한 문제) 금방 해결하고 완성을 했습니다!! 그 밖에는 문제된 일은 없습니다. 제가 스스로 작성한 코드가 작동하는 것을 보니 정말 뿌듯하네요. 그리고 예시와 다르게 UI를 상당히 많이 바꿨습니다...! 미션을 하면서 가장 즐거웠던 과제였습니다👍 React 미션 04. Next, TypeScript을 이용한 퀴즈 앱이번 프로젝트에서는 라우팅을 처리하기 위해 사용한 useRouter에서 문제가 발생했습니다. 문제를 해결하려고 찾아보니, next/router 대신에 next/navigation을 사용해야 한다는 것을 알게 되었습니다. 그것을 마지막으로 문제 없이 잘 과제를 끝 맞추었습니다.회고워밍업 스터디를 처음 시작할 때에는 HTML/CSS, JS의 기초만 약간 알고 있었고, 부트캠프나 프로젝트 경험이 없었습니다. 또한, 외적인 사정과 긴 강의 시간, 다양한 미션으로 인해 시간적으로도 부족함을 느꼈습니다. 하지만 저 자신의 발전을 위해서 새벽 3~4시까지 시간을 투자해나가며 수업과 미션을 해결해나갔습니다.또한, 저는 코틀린을 사용한 경험이 있는데, 타입 스크립트가 코틀린과 유사하다는 느낌을 받아서 타입 관련 공부가 수월했습니다. 하지만 타입스크립트의 심화 부분과 Next.js, 리덕스 등에 대해서는 추가적인 공부가 필요하다고 생각하고 있습니다.3주간의 스터디는 성장감을 느끼게 해주었습니다. 그렇기에 이미 끝났다는 사실이 아쉽기도 합니다. 이 소중한 경험을 제공해 준 인프런과 스터디를 이끌어주신 강사님에게 깊은 감사의 인사를 전합니다.앞으로도 계속해서 성장하고 발전하기 위해 노력할 것이며, 새로운 도전들을 마주하면서 더욱 성장해 나가고 싶습니다. 감사합니다! 🌱

프론트엔드워밍업클럽FE

장서윤

[인프런 워밍업 클럽 0기] 1주차 발자국 👣

✅ 학습 내용1일차자바스크립트 기초Window 객체 및 DOMEvent2,3 일차자바스크립트 중급 4일차객체지향 프로그래밍(OOP), 비동기5일차Iterator + Generator디자인 패턴 ✅ 미션 과정1⃣ 미션 메뉴mock data 생성하면서 💡고민사항이 생겼다.[ { "name": "비빔밥", "country": "Korea", "imageUrl" : "./images/food/korea/bibimbap.png", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, ... 중략 " }, ] 💡 description 에는 무슨 내용을 넣을 것인가?해당 음식과 관련된 내용을 찾아서 넣는다로렘 입숨을 넣는다. => html, js로 기능 구현이 목표이기 때문에 아무 의미 없는 텍스트, 로렘 입숨을 넣어줬다.💡 image 폴더구조는 적절한가?폴더 구조는 항상 고민인 것 같다. 사실상 해당 프로젝트에는 food관련 image만 들어가기 때문에, images/korea/~ 등으로 food라는 폴더가 중첩해도 있지 않아도 될 것이다. 그런데 만약 다른 유형의 image가 들어간다면,,?(background image 등) 폴더 구조를 엎어야할 것이다.=> 이 부분은 개발해보면서 다른 이미지가 넣을지 말지가 결정될 때 정할 것이다!✅ 회고사실 아직 강의와 미션을 전부 완료하진 못했다... 커리큘럼 그대로 진행해야지!를 목표로 삼았으나, 계획대로 지켜지지 못해 아쉬울 따름이다. 다음 발자국은 조금 더 의미있는 내용을 기록하고 싶다.  

웹 개발워밍업FE프론트

학생

[3주차 발자국] 인프런 워밍업 클럽 스터디 0기 FE

회고벌써 마지막 발자국이다.3주를 달려오면서 느낀 것은, 시간 분배를 제대로 해야 한다는 것이다.초반에 자바스크립트에 시간을 쓰느라 리액트 파트를 느긋하게 하지 못하고 빠르게 돌았다.우선은 앞에서 풀리지 않은 문제가 있더라도 제때 진행해야 하는 진도를 끝내놓고 일요일쯤에 다시 돌아오는 편이 나았을 것이라는 생각이 들었다. 스터디가 끝나도 이 점은 기억하여 그날 해야하는 일은 마치고 나서 모르는 점을 보충하는 시간을 주말이나 저녁에 따로 가지는 것이 좋겠다.사실 자바스크립트부터 모두 이해하고 미션도 다 하고 싶었지만, 아무래도 기간 안에 전부 끝내지는 못할 것 같다.특히나 적어도 수료를 하려면 리액트 과제를 4개는 해야 하는데, 아직 1개 완료, 1개 진행중이다.내일까지 과제를 3개 완료할 수 있을지 모르겠다. 그래도 최대한 해볼 생각이다.지금 디즈니 플러스 앱의 github 로그인 버튼 기능을 구현하는것에서 막혀 머리를 싸매고 있다. 깃허브 계정 로그인까지는 되는데 자꾸 페이지 이동이 안된다.기본 개념부터 하나하나 느긋하게 보고 이해하기에는 시간이 부족하다고 느껴 쉽게 설명한 문서가 있는지 찾아다니는데에 시간을 꽤나 쓴 것 같다. 공식 문서부터 다시 보고 내일까지 성공하길 바라고 있다.스터디가 끝나면 강의에서 어려웠던 부분 혹은 시간을 들여 보지 못했던 부분을 다시 해보면서 역량을 쌓아야겠다.강의 요약이번주 강의 범위: 섹션6~7(React TDD), 섹션8(Next.js, TypeScript), 섹션9~10(Redux)React TDDNext.jsTypeScriptRedux React TDD: 테스트 주도 개발(TDD, Test Driven Development): 테스트 코드를 작성한 후 그것을 Pass할 수 있는 실제 코드를 작성하는 개발 방식장점: 소스코드 안정감 부여, 디버깅 및 개발 시간 감소, 클린 코드 가능성 높음 React Testing LibraryReact 컴포넌트를 테스트하는 가벼운 솔루션. 행위 주도 테스트(태그보다는 이벤트 발생 시 화면 변화 등의 테스트)Jest React Testing Library와 함께 React 테스트에 쓰이는 테스팅 프레임워크.test Case를 만들어 확인.단위 테스트를 위함 Jest 파일 구조describe: 여러 관련 테스트를 그룹화하는 블록을 만듦.it: ==test. 개별 테스트를 수행하는 곳.expect: 값을 테스트할때마다 사용됨. matcher와 함께 사용.matcher: 다른 방법으로 값을 테스트하도록 함.쿼리함수get: 요소가 없으면 오류 발생find: 요소가 없으면 null 반환query: 요소가 없으면 거부. 요소가 있으면 Promise 반환.테스팅 검사 관련 모듈ESLint: 문법오류 잡기Prettier: 코드 형식 맞추기Next.js: React의 SSR(Server Side Rendering) 구현을 도와주는 프레임워크. (React는 라이브러리)(리액트에서도 SSR을 지원하지만 구현하기에 굉장히 복잡. 따라서 NextJS 사용.)React는 CSR(Client Side Rendering) 이용CSR: JS가 다운로드된 후에야 화면 표시 및 기능 활성화됨첫페이지에서 빈 html을 가져와 JS파일을 해석하여 화면을 구성하기에 포털 검색에 거의 노출될 일이 없음. => 검색엔진 최적화(SEO) 불리SSR: 서버에서 Pre-Rendering된 HTML 제공사용자와 검색엔진 크롤러에게 바로 렌더링 된 페이지를 전달할 수 있게 됨.Pre-Rendering: initial Load(html보임) -> JS로드 -> Hydration(컴포넌트 활성화)Data FetchingReact에서 데이터를 가져오는 방법useEffect 내부에서 가져옴Nextjs에서 데이터를 가져오는 방법getStaticPropsgetStaticPathsgetServerSideProps TypeScript: JavaScript에 타입을 부여한 언어. 오픈소스.사용하는 이유단순화. 쉽게 읽고 디버그 가능코드 유형 검사를 통해 JavaScript에서의 일반적인 버그 피하는데에 도움Compile브라우저에서 실행하기 위해 파일을 변환해주는 것TypeScript에서 하는것. JavaScript에서는 안함.  TypeScript Typevalue가 가진 프로퍼티나 함수를 추론할 수 있는 방법TypeScript 유형Primitive types: string, number, boolean, null, undefined, symbolObject types: function, array, class, object추가 제공 타입: Tuple, Enum, Any, Void, Never, Uniontype annotation: 개발자가 타입스크립트에게 타입을 알려줌type inference: 타입스크립트가 타입을 추론하는 것type assertion: 타입스크립트의 추론을 막는다. 나의 주장에 대해 의심하지 마라.Redux: JavaScript Application을 위한 상태관리 라이브러리.Redux Data Flow Dispatch Action -> Call Reducer -> Update Store -> RenderAction: 자바스크립트 객체. 작업유형 지정하는 정보 들어있음.Reducer: 이전 state와 action을 받은 후 next state를 반환하여 store를 업데이트.Redux Store: 앱의 전체 상태 트리를 갖는 저장소. 몇 가지 Methods가 있는 객체.Provider컴포넌트들에서 Redux Store에 접근할 수 있도록 해줌.컴포넌트들을 둘러싸고 최상위 수준에서 렌더링.useSelector: useSelector Hooks를 사용해 스토어의 값을 가져옴.useDispatch: dispatch함수에 접근하는 Hooks. (Action을 보냄)미들웨어Action을 Dispatch하고 Reducer에 도달하는 순간 사전에 지정된 작업 실행 도와주는 중간자Reducer에 도달하기 전에 API와 통신을 하고 그것을 전달.Redux Thunk리덕스 미들웨어. 비동기 작업할 때 많이 쓰임.Thunk: 일부 지연된 작업을 수행하는 코드 조각Redux Toolkit로직을 작성하기 위한 공식 권장 접근 방식.모범 사례를 이용해 작업 단순화 및 실수 방지. 미션 해결 과정[1. 자바스크립트 미션 보강]미션4(Day5) 책 리스트 나열 앱의 보강이 필요했던 부분스타일 보강알림때문에 화면이 아래로 밀리는 문제 고치기 스타일 보강포인트 1. input의 넓이 키우기: 가로는 화면의 대부분을 가로지르도록, 세로는 h1정도로 크게포인트 2. 제출 버튼을 책 저자 input란 아래에 두어 보기 좋게 만들기(가로도 길게)포인트 3. hr로 입력Form과 책 리스트 Form 구분하기포인트 4. 책 리스트 Form 좀 더 보기 좋게 만들기(보강 전)(보강 후)input의 font size 조정을 이용해 포인트1 해결display: flex; flex-direction: column을 이용해 포인트2 해결<hr>을 삽입해 포인트3 해결위치 및 폰트 미세 조정하여 포인트4 해결알림때문에 화면이 아래로 밀리는 문제 고치기책이 추가되었습니다, 아이디가 입력되지 않았습니다 등의 알림을 뜨게 하면 아래 화면이 움직이는 것 때문에 불편함이 있었다. 예제 동영상을 다시 보니 이 부분은 나와 똑같이 구현되어있기는 했지만, 편의성을 위해 바꿔보았다.알림이 아래로 추가되도록 하지 않고, display: none을 지정했다 풀었다 하는 방식으로 겹쳐 표시setTimeout에 clearTimeout을 이용해 알림을 매번 새로 3초 지속시간 부여float: right 속성을 이용해 '책' title의 오른쪽에 배치 미션5(Day5) Github Finder 만들기의 보강이 필요했던 부분API 통신스타일API 통신github의 사용자들을 검색하려면 github에서 제공하는 api를 fetch하면 된다고 한다.기본적인 형태는 이런 식이다.async function logJSONData() { const response = await fetch("https://api.github.com/users"); const jsonData = await response.json(); console.log(jsonData); }https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Using_Fetch내가 이해한 부분은, fetch 안에 api url을 넣고, fetch가 반환하는 response에 .json()을 해주면 데이터를 가져올 수 있다는 것이다.github에서 사용자를 검색하는 데에는 특별한 인증 키가 필요 없다고 한다.https://www.daleseo.com/github-rest-api/진도가 밀리고 있기에 여기까지 이해하고 잠시 멈추고 리액트 공부를 시작했다. [2. React 미션 해결 과정]미션8(Day9) 예산 계산기 앱 만들기이 미션은 React 섹션 1~3을 차근차근 복습할 겸 따라하며 필요한 부분을 알맞게 바꿔 만들었다.첫 번째 커밋에서는 class component 방식으로, this.state와 this.setState를 사용해 App.js에 몽땅 코딩했다.총 지출을 출력하는 부분에서 계속 금액이 문자열로 지정되어 합쳐지는 것을 막기 위해 일일이 Number()처리를 했다.그리고 두 번째 커밋에서는 function component를 이용하는 React Hooks(class component 없이 state를 사용할 수 있도록 하는 기능)를 이용했다.즉 React Hooks에서는 component 분리(App.js, Input.js, Lists.js, List.js), useState 사용.항목에 마우스 올릴 시 크기가 커지는 효과: scale과 transition 조정(아래 블로그를 참고했다.)https://record-than-remember.tistory.com/entry/CSS-%EC%9D%B4%EB%AF%B8%EC%A7%80-hover-transformscale-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%98%A4%EB%B2%84%EC%8B%9C-%ED%99%95%EB%8C%80수정 버튼을 구현하는 것이 가장 어려웠다.Input component에서 수정 기능을 구현하고 싶었지만 자꾸 에러가 떠서 list component에서 그냥 state를 만들고 구현했다. 이 부분은 다음에 시간 여유가 있을 때 보강해야겠다. 미션9(Day10) 디즈니 플러스 앱 만들기미션을 하면서 떠올린 단어는 다음과 같다. '막막하다'...물론 거의 대부분이 강의에서 다루었던 넷플릭스 클론 코딩과 같은 형태였지만,제대로 체화하지 못했을 뿐더러 CSS를 적용하고 왔다갔다하면서 수정하는 것은 내 노트북에게는 상당히 힘든 일이었다.(저장할때마다 체감상 기본 10초는 멈췄던 것 같다. 노트북을 당장 바꿔야겠다.)기간안에 전부 이해하고 처음부터 만들기는 힘들 것 같으므로, 넷플릭스 클론을 할 때 사용했던 코드를 수정하는 쪽으로 미션을 수행했다.해야하는 일은 다음과 같다.넷플릭스 흔적 지우고 로고, bgcolor, nav color 디즈니 비슷하게 바꾸기 (완료)LoginPage 구현하기 (완료)깃허브 로그인 버튼 구현하기 (진행중)Category 컴포넌트 만들기 (완료) 현재 깃허브 OAuth 토큰을 이용해 로그인해서 code를 받는것까지는 진행된 것 같은데, 로그인 후에 페이지 전환하는 것에서 막혔다. useState를 사용해 로그인이 되었을 경우와 안되었을 경우를 나누어 다른 컴포넌트를 라우팅하도록은 해놓았는데, 깃허브 액세스 토큰을 어떻게 사용하고 데이터를 어떻게 가져오는지 등을 아직 이해하지 못했다. 그리고 그 기능끼리 연결을 어떻게 시키는지 아직 모르겠다. 그래도 내일까지는 완성하고싶다.

프론트엔드인프런워밍업클럽스터디FE0기

이양구

[인프런 워밍업 클럽 FE 0기] 미션7 - 예산 계산기 앱

💸 Budget Calculator APP GitHub 💸 Budget Calculator APP DemoRecord by ScreenToGif  개요인프런 워밍업 클럽 FE 0기의 일곱 번째 미션인 '예산 계산기 앱' 입니다. 따라하며 배우는 리액트 섹션 0~3(To-Do 앱) 목표의존성 배열(Dependency Array) 을 이용해 함수 실행하기state 를 전역 변수처럼(?) 사용해보기 구현구조|-- App | |-- Form | |-- Lists | | |-- List  의존성 배열(Dependency Array) 을 이용해 함수 실행하기// App.jsx const [budgetList, setBudgetList] = useState( JSON.parse(localStorage.getItem("budgetList")) || [] ); useEffect(() => { localStorage.setItem("budgetList", JSON.stringify(budgetList)); }, [budgetList]); const totalCost = useCallback(() => { return budgetList.reduce((acc, cur) => acc + cur.cost, 0); }, [budgetList]); 최상위 컴포넌트인 <App> 컴포넌트에서 만든 'budgetList'이라는 state를 useEffect와 useCallback의 의존성 배열에 추가했다.useEffect에서는 해당 state가 변경되면 로컬 스토리지의 budgetList를 최근의 리스트로 변경한다.이렇게 하면 일일이 setBudgetList가 호출되는 곳마다 함수를 사용하지 않아도 된다.다음은 예산의 총 금액을 반환하는 함수가 리스트가 변경될 때마다 실행되도록 useCallback으로 감싸고 의존성 배열에 state를 추가했다.// Form.jsx const budgetNameRef = useRef(); const [budgetName, setBudgetName] = useState(""); const [budgetCost, setBudgetCost] = useState(0); useEffect(() => { if (isEdit) { setBudgetName(budget.name); setBudgetCost(budget.cost); budgetNameRef.current.focus(); } }, [isEdit]); <Form> 컴포넌트에서는 useEffect에 'isEdit'이라는 state를 의존성 배열에 추가했다.사용자가 예산을 수정하기 위해 list의 Edit 버튼을 클릭하면 해당 budget의 name과 cost를 최근 state로 불러오고, useRef를 이용해 name을 입력하는 <input> 요소에 focus 상태가 되도록 했다.state 를 전역 변수처럼(?) 사용해보기// App.jsx const [currentBudget, setCurrentBudget] = useState({ isEdit: false, budget: {}, }); // List.jsx const handleEdit = () => { setCurrentBudget({ isEdit: true, budget: list, }); setHandleStatus({ type: "edit", message: "Editing..." }); }; // Form.jsx const handleBudgetSubmit = (e) => { const newBudget = { id: Date.now(), name: budgetName, cost: budgetCost, }; // isEdit의 값에 따라 새로 추가할지 수정할지 결정 if (isEdit) { setBudgetList((prevBudgetList) => { const newBudgetLists = [...prevBudgetList]; const index = newBudgetLists.findIndex(({ id }) => id === budget.id); newBudgetLists[index] = newBudget; return newBudgetLists; }); setCurrentBudget({ isEdit: false, budget: {} }); setHandleStatus({ type: "submit", message: "Edit Success!" }); } else { setBudgetList((prevBudgetLists) => [...prevBudgetLists, newBudget]); setHandleStatus({ type: "submit", message: "Submit Success!" }); } // submit 종료 시 input의 데이터를 초깃값으로 설정 setBudgetName(""); setBudgetCost(0); }; 배웠던 To Do 앱은 List의 Edit 버튼을 클릭했을 때 해당 List의 요소를 input 요소로 변경시키고 수정을 했다.하지만 과제는 클릭을 했을 때 List의 요소를 변경시키는 게 아니라 Form의 input에 해당 예산의 데이터를 전달해야 했다.그래서 마치 전역 변수처럼 사용할 'currentBudget'이라는 state를 생성하고 'isEdit'이라는 boolean 값과 수정할 예산의 데이터를 담을 'budget'이라는 값을 설정했다.'isEdit'의 상태 값이 true일 때 수정하기와 삭제하기 <button> 요소를 disabled로 변경한다.또한 submit 함수는 새로운 입력 값을 budgetList에 추가하지 않고 해당 예산의 index를 찾아 수정하고 리스트를 변경한다.이렇게 하니 onSubmit과 onEdit 처럼 비슷한 기능을 하는 함수를 여러 개 만들지 않아도 되었다. ⚠ setTimeout 렌더링const { type, message } = handleStatus; const handleStyle = useCallback(() => { if (type === "edit") { return "text-gray-500 block"; } else if (type === "none") { return "hidden"; } else { // 2초 뒤에 실행 --> App - Form - Status 1번 더 렌더링 setTimeout(() => { setHandleStatus({ type: "none", message: "" }); }, 2000); if (type === "submit") { return "text-green-400 block"; } else { return "text-red-400 block"; } } }, [type]); 추가, 삭제, 수정의 완료 및 진행 중 상태를 보여주는 <Status> 컴포넌트를 만들었다.App에서 만든 'handleStatus'라는 state를 전달하고 메세지가 나타난 뒤에 사라지게 만들고 싶어서 setTimeout() 메서드를 이용해 2초 뒤에 상태를 초기화했다.하지만 이 상태가 App과 Form 컴포넌트에서 참고하다 보니 나타나고 사라질 때마다 렌더링이 발생했다.CSS의 opacity로 처리하기엔 state의 값을 변경해야 했기에 알맞는 방법은 아니라 생각했다.뭔가 <Status> 컴포넌트 내부에서만 렌더링이 일어나게 하고 싶었는데 아직 다른 방법을 찾지 못했다.😢😢😢 회고다른 컴포넌트의 클릭 이벤트로 변경된 state를 이용하는 부분이 생각보다 오래 걸렸다.처음엔 콜백 함수처럼 App 컴포넌트에서 함수 만들고 prop으로 넘겨봤지만 List와 Form은 종속적인 관계가 아니라 힘들었다. 😢그래서 생각해낸 게 state를 이용해서 상태의 변경을 이벤트처럼 사용하는 것이었다.pub-sub 혹은 observer 패턴 같다는 생각도 했지만, 이렇게 최상위에서 선언한 state가 이곳저곳 돌아다니는 게 좋은 방법은 아닐 것 같다는 생각이 들었다.규모가 커지면 렌더링 관리도 힘들고 props를 쫓아다녀야 하기 때문이다.이래서 상태 관리 라이브러리가 나왔나 보다. 🤔 

프론트엔드워밍업워밍업클럽프론트엔드프론트FE미션과제발자국

이양구

[인프런 워밍업 클럽 FE 0기] 미션4-2 - GitHubFinder 앱

🔍 github-finder-app GitHub 🔍 github-finder-app 개요인프런 워밍업 클럽 FE 0기의 네 번째 미션인 'GitHubFinder 앱' 입니다.따라하며 배우는 자바스크립트 섹션 5(OOP), 섹션 6(비동기) 목표Fetch API 를 이용해 깃허브 유저 목록 불러오기Closure 를 이용해 Debounce Function 만들기 구현Fetch API 를 이용해 깃허브 유저 목록 불러오기async function loadUser(input) { prevInputValue = input; try { // const response = await fetch('./src/javascript/user.json'); const response = await fetch(`${url}/${input}`); if (!response.ok) { throw new Error('Failed to fetch user json'); } const json = await response.json(); setUserAvatar(json); setUserInfo(json); await loadUserRepos(json); } catch (error) { console.error(error); } }fetch() 메서드의 응답은 HTTP 응답 전체를 나타내는 'response' 객체를 반환한다.response의 ok 속성은 응답의 성공 여부를 불리언 값으로 가지고 있다.따라서 응답이 성공이 아닐 경우 오류 객체(new Error())를 반환하고 catch 문으로 Promise의 오류를 처리한다.응답에 성공한 response 객체를 JSON으로 사용하기 위해선 json() 메서드를 이용해 파싱해야 한다. Closure 를 이용해 Debounce Function 만들기// debounce debounceInput.addEventListener('input', debounce(loadUser, 1000)); // debounceInput.addEventListener('input', e => callback(e)); function debounce(callback, delay = 0) { // timer는 부모 함수에서 선언된 지역 변수 let timer = null; return (arg) => { // 여기서 arg는 input event if (timer) { // 이미 타이머가 있는데 또 실행되면 타이머 삭제 clearTimeout(timer); } // 변수 timer는 부모 함수에서 선언되었지만 내부 함수에서 사용(클로저) timer = setTimeout(() => { callback(arg.target.value); }, delay); }; }<input> 요소의 'input' 이벤트는 요소의 value가 변경될 때마다 발생한다.만약 사용자가 입력할 때마다 서버에 데이터를 요청한다면 서버의 부하가 커지기 때문에 좋은 방법은 아니다.이럴 때 사용자의 입력이 끝난 뒤 마지막 value를 이용해 서버로 요청하는 게 효율적인 방법이라 할 수 있다.함수의 실행 요청이 반복될 때 마지막 요청만으로 실행하는 걸 '디바운싱(debouncing)'이라고 부른다.debounce 함수는 인자로 실행할 함수를 받고 자식 함수를 반환한다.부모 함수인 debounce 함수에서 선언한 변수(timer)를 자식 함수에서 사용할 수 있는 클로저(Closure)를 이용해 자식 함수의 setTimeout() 메서드의 반환 값인 'timeoutID'를 할당한다.변수 'timer'에 할당한 timeoutID를 이용해 setTimeout() 메서드의 지연 시간(delay)이 종료되기 전에 요청이 들어왔다면 이전에 생성한 타이머를 clearTimeout() 메서드를 이용해 종료하고 다시 타이머를 할당한다. 이렇게 delay로 설정한 시간 이내에 사용자의 입력이 없을 경우 API 요청 함수를 실행하게 된다. 반복적인 함수의 실행을 다루는 방법으로 디바운싱(debouncing)와 쓰로틀링(throttling)이 있다.여러 변수를 고려해 'lodash' 라이브러리의 debounce를 많이 사용한다. 회고이번 미션은 debounce가 반환하는 자식 함수의 인자(argument)가 어떤 타입인지 알기 때문에 callback 함수에 전달하는 인자를 수정해서 미숙한 debounce 함수라고 볼 수 있다.늘 라이브러리를 통해 사용하던 함수를 만들려고 하니 모르는 것도 많고, 고려해야 할 부분이 많다는 걸 알게 됐다.자바스크립트의 기초를 잘 알아야 이런 라이브러리 메서드의 원리를 이해하기도 쉽고, 커스텀하기에 수월한 것 같다.(외의로 GitHub의 API 요청이 API key 없이도 되어서 신기했고, 그 덕에 조금은 수월했다. 아주 조금... 😵) DemoRecord by ScreenToGif

프론트엔드워밍업워밍업클럽프론트엔드프론트FE미션과제발자국

jikwan12

[ 인프런 워밍업 클럽 FE 0기 ] 1주차 발자국

2024 새로운 도전을 하기 위해 여러 사이트를 돌아다니던 중 인프런 워밍업 클럽을 발견하게 되었습니다. 마침 저는 취업 준비를 앞두고 있고 자바스크립트 기초 지식을 단단하게 다져 취업을 준비해야겠다는 생각이 들었고 이러한 좋은 기회를 놓칠 수 없어 바로 신청하게 되었습니다.강의 요약01. 자바스크립트 기초[ console 객체 ]JavaScript에서 제공하는 내장 객체 중 하나이다.이 객체를 사용하면 개발자는 브라우저의 콘솔이나 터미널에 다양한 정보를 출력하고 디버깅할 수 있다.다양한 메서드를 제공하여 개발자가 원하는 정보를 출력하거나 디버깅할 때 유용하다.console 객체 일부 메서드log(): 콘솔에 일반적인 로그 메시지를 출력error(): 콘솔에 에러 메시지를 출력warn(): 콘솔에 경고 메시지를 출력 table(): 테이블 형태로 데이터를 출력time()과 timeEnd(): 특정 코드 블록의 실행 시간을 측정  [ let, var, const]let:블록 범위(block-scoped) 변수를 선언변수를 선언한 블록 내에서만 접근 가능변수의 값은 재할당이 가능같은 이름의 변수를 중복해서 선언할 수 없음var:함수 범위(function-scoped) 변수를 선언변수를 선언한 함수 내에서만 접근 가능변수의 값은 재할당이 가능같은 이름의 변수를 중복해서 선언할 수 있음호이스팅(hoisting)이 발생하여 변수 선언이 해당 스코프의 최상단으로 끌어올려짐const:블록 범위(block-scoped) 상수를 선언변수를 선언하고 초기값을 할당한 후, 재할당이 불가능변수의 값이 변하지 않아야 할 때 사용같은 이름의 변수를 중복해서 선언할 수 없음 [ 호이스팅 ]JavaScript에서 변수 선언과 함수 선언을 해당 스코프의 최상단으로 끌어올리는 동작변수 호이스팅의 예시console.log(x); // undefined var x = 5; console.log(x); // 5위 코드에서 var x = 5; 문장 이전에 console.log(x);를 호출하고 있다. 하지만 결과는 undefined가 출력된다. 이는 변수 x의 선언이 호이스팅에 의해 해당 스코프의 최상단으로 끌어올려진 후, 초기값이 할당되기 전까지 undefined로 초기화되기 때문이다. 함수 호이스팅의 예시sayHello(); // "Hello!" function sayHello() { console.log("Hello!"); }위 코드에서 sayHello() 함수를 호출하기 전에 함수를 선언하고 있다. 이는 함수 선언이 호이스팅에 의해 해당 스코프의 최상단으로 끌어올려진 후, 호출 위치와 상관없이 함수를 사용할 수 있게 된다. [ 타입, 타입 변환 ]자바스크립트의 타입은 다음과 같음원시 타입: Boolean, String, Number, null, undefined, Symbol참조 타입: Object, Array  원시타입은 고정된 크기로 Call Stack 메모리에 저장되며 실제 데이터가 변수에 할당된다.참조타입은 데이터 크기가 정해지지 않고 Call Stack 메모리에 저장되며 데이터의 값이 Heap에 저장되며 메모리의 주소 값이 할당된다. 타입 변환 유형명시적 데이터 변환: 직접 함수를 통한 변환자동 데이터 변환: 자바스크립트 자체에 의해 자동으로 변환 [ 연산 및 Math Object ]Math object에서 제공하는 다양한 메서드들을 이용해 다양한 연산을 할 수 있다.Math 일부 메서드Math.abs(x):주어진 숫자의 절댓값을 반환합니다.예: Math.abs(-5)는 5를 반환합니다.Math.ceil(x):주어진 숫자보다 크거나 같은 정수 중 가장 작은 수를 반환합니다.예: Math.ceil(4.2)는 5를 반환합니다.Math.floor(x):주어진 숫자보다 작거나 같은 정수 중 가장 큰 수를 반환합니다.예: Math.floor(4.8)는 4를 반환합니다.[ Template Literals ]Template literal은 JavaScript에서 문자열을 작성하는 방법으로, 백틱(``)을 사용한다. 이를 통해 변수나 표현식을 쉽게 삽입하고 멀티라인 문자열을 작성할 수 있으며, 특수 문자 처리에 용이하다. ${}를 활용하여 문자열에 변수를 할당할 수 있다.[ Loops ]for: 코드 블록을 여러 번 반복for/in: 객체 속성을 따라 반복while: 지정된 조건이 true 인 동안 코드 블록을 반복do/while: 조건이 true인지 검사 전에, 코드 블록을 한 번 실행시킨다. 그 이후 while 반복문 실행02. Window 객체 및 DOM[ window 객체 ]window 객체는 웹 브라우저 창을 나타내는 전역 객체입니다.JavaScript에서 모든 전역 변수와 함수는 window 객체의 프로퍼티 또는 메서드로 접근할 수 있습니다. 예를 들어, window.alert()와 window.innerWidth와 같이 사용할 수 있습니다.브라우저 창의 크기, URL 정보, 타이머, 팝업 창 등 다양한 기능을 제공합니다.window 객체는 생략 가능하므로, alert()와 innerWidth와 같이 사용하는 것도 가능합니다.[ DOM ]DOM은 HTML, XML 또는 XHTML 문서의 구조를 표현하는 객체 지향적인 표현 방식입니다.웹 페이지의 요소(element)들을 트리 구조로 표현하며, 각 요소는 노드(node)라고 불리는 객체로 표현됩니다.DOM을 통해 JavaScript는 문서의 구조, 스타일, 내용 등을 동적으로 조작할 수 있습니다.DOM은 HTML 요소에 접근하고 조작하기 위한 다양한 메서드와 속성을 제공합니다. 예를 들어, getElementById(), querySelector(), innerHTML 등을 사용하여 요소를 선택하거나 내용을 변경할 수 있습니다.DOM은 웹 페이지의 상호작용과 동적인 변경을 가능하게 해주는 핵심적인 요소로, JavaScript에서 웹 페이지와의 상호작용을 구현하는 데 사용됩니다.03. Event[ Event ]JavaScript에서 이벤트는 웹 페이지나 애플리케이션에서 발생하는 사용자의 동작이나 시스템 상태 변화를 감지하고, 이에 대한 응답을 처리하는 메커니즘입니다. 이벤트는 HTML 요소나 JavaScript 객체에서 발생할 수 있으며, 특정 동작이나 상태 변화에 대응하여 코드를 실행할 수 있도록 도와줍니다.[ Event Bubbling과 Event Capturing ]이벤트 버블링(Event Bubbling)과 이벤트 캡처링(Event Capturing)은 HTML 요소 간에 중첩된 구조에서 이벤트가 전파되는 방식을 설명하는 개념입니다.이벤트 버블링 (Event Bubbling):이벤트 버블링은 이벤트가 발생한 요소에서 시작하여 상위 요소로 전파되는 현상을 의미합니다.예를 들어, <div> 요소 안에 <p> 요소가 있고, <p> 요소를 클릭할 때 해당 이벤트가 발생하면, 이벤트는 <p> 요소에서 시작하여 <div> 요소, 그리고 상위 요소들로 전파됩니다.이벤트 버블링은 부모 요소로 이벤트가 전파되므로, 상위 요소에서도 동일한 이벤트를 처리할 수 있습니다.이벤트 리스너를 상위 요소에 등록하면 하위 요소에서 발생한 이벤트도 감지할 수 있습니다.이벤트 캡처링 (Event Capturing):이벤트 캡처링은 이벤트가 발생한 요소에서 시작하여 상위 요소로 전파되는 현상과 반대로, 상위 요소에서 시작하여 이벤트가 발생한 요소까지 전파되는 현상을 의미합니다.이벤트 캡처링은 이벤트가 발생한 요소의 최상위 부모 요소부터 시작하여 하위 요소로 전파됩니다.이벤트 캡처링은 이벤트 리스너를 상위 요소에 등록하고, 하위 요소에서 발생한 이벤트를 상위 요소까지 전파하여 처리할 수 있습니다. [ Event Delegation ]이벤트 위임(Event Delegation)은 상위 요소에 이벤트 리스너를 등록하여 하위 요소들의 이벤트를 처리하는 방식 04. 자바스크립트 중급[ this ]Method의 this: 해당 객체를 가리킨다.함수에서 this: window 객체를 가리킨다.constructor의 this: 빈 객체를 가리킨다. [ bind, call, apply]call():함수를 호출하는 함수.첫 번째 인자 값으로 어떠한 것을 전달해 주면 호출되는 함수의 this가 인자 값으로 지정apply(): call()과 유사하나 인수 부분에 배열을 넣어줘야함.bind(): 해당 함수가 지정한 인자 값을 가리키도록 하지만 call(), apply()와 다르게 직접 함수 실행 X [ 삼항 연산자 ]? 앞의 조건을 기준으로 조건이 참이면 : 앞의 있는 부분을 반환하고 거짓이면 뒷부분 반환 [ Event Loop]이벤트 루프(Event Loop)는 JavaScript에서 비동기 작업을 처리하는 동작 메커니즘입니다. 이벤트 루프는 단일 스레드 환경에서 비동기 작업을 효율적으로 처리하기 위해 사용됩니다.[ Closure ]다른 함수 내부에 정의된 함수가 있는 경우, 외부 함수가 실행을 완료하고 해당 변수가 해당 함수의 외부에서 더 이상 엑세스할 수 없는 경우에도, 해당 내부 함수는 외부 함수의 변수 및 액세스가 가능하다. 이 기능을 Closure라고 부른다.[ 구조 분해 할당 ]배열이나 객체의 속성을 분해하여 사용할 수 있는 형태로 만드는 것[ Map, Filter, Reduce ]Map, Filter, Reduce은 배열 메서드의 대표적인 예시이다.map(): 배열 내의 모든 요소 각각에 대하여 동작을 수행하고 나온 결과를 새로운 배열로 만들어 반환한다.filter(): 기존 배열의 필터를 통과하는 요소만을 새로운 배열로 반환한다.reduce(): 배열의 각 요소에 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결괏값을 반환한다.[ 얕은 복사 VS 깊은 복사 ]깊은 복사(Deep Copy): 원본 객체의 모든 내용을 복사하여 새로운 객체를 생성합니다. 얕은 복사(Shallow Copy): 원본 객체의 참조만을 복사하여 동일한 객체를 가리키는 새로운 객체를 생성합니다.[ 함수 표현식, 함수 선언문 ]함수 선언문: 함수 선언은 함수를 만들고 이름을 지정하는 것이다.일반적인 함수 선언 방식으로 function 키워드와 식별자를 표기하여 사용한다.함수 표현식은 함수를 만들고; 변수에 할당하는 것이다.익명 함수(function 키워드는 사용했으나 식별자 X), 화살표 함수 사용05. OOP객체 지향 프로그래밍(Object-Oriented Programming, OOP)은 프로그래밍 패러다임 중 하나로, 현실 세계의 개념과 개체들을 프로그램의 구조로 모델링하는 방식입니다. OOP는 코드의 재사용성, 유지보수성, 확장성 등을 향상시킬 수 있는 다양한 개념과 기법을 제공합니다.OOP의 주요 개념은 다음과 같습니다:클래스(Class):클래스는 객체를 생성하기 위한 설계도 또는 템플릿으로, 속성(멤버 변수)과 동작(메서드)을 포함합니다.예를 들어, '사람' 클래스는 '이름'과 '나이'라는 속성을 가지고 '인사하기'라는 동작을 수행할 수 있습니다.객체(Object):클래스의 인스턴스로, 실제로 메모리에 할당된 개체입니다.객체는 클래스에서 정의한 속성과 동작을 가지며, 각각의 객체는 독립적인 상태를 유지합니다.'사람' 클래스의 객체로는 '철수'나 '영희' 등이 있을 수 있습니다.상속(Inheritance):상속은 클래스 간의 계층 구조를 구성하여 코드의 재사용성을 높이는 개념입니다.부모 클래스(상위 클래스)의 속성과 동작을 자식 클래스(하위 클래스)가 물려받아 사용할 수 있습니다.상속을 통해 일반적인 기능을 가진 부모 클래스를 정의하고, 이를 기반으로 특화된 자식 클래스를 만들 수 있습니다.다형성(Polymorphism):다형성은 동일한 메서드를 다양한 방식으로 호출할 수 있는 개념입니다.하나의 메서드가 다른 클래스의 객체에 따라 다른 동작을 수행할 수 있습니다.다형성은 상속과 함께 사용되어 코드의 유연성과 확장성을 높여줍니다.캡슐화(Encapsulation):캡슐화는 관련된 속성과 동작을 하나의 단위로 묶는 개념입니다.클래스는 내부의 속성과 동작을 외부로부터 숨기고, 외부에서는 제공된 인터페이스를 통해 클래스와 상호작용합니다.캡슐화는 데이터의 은닉과 코드의 모듈화를 가능하게 합니다.06. 비동기[ 동기 비동기 ]동기(Synchronous)는 작업을 순차적으로 실행하고, 이전 작업이 완료될 때까지 다음 작업을 실행하지 않는 방식입니다. 작업이 순서대로 진행되기 때문에 실행 순서가 보장되며, 결과를 예측하기 쉽습니다. 하지만 작업이 완료될 때까지 대기해야 하므로 다른 작업을 처리하는 동안 시간이 지체될 수 있습니다.비동기(Asynchronous)는 작업을 동시에 실행하고, 이전 작업의 완료 여부와 상관없이 다음 작업을 실행하는 방식입니다. 작업이 백그라운드에서 동시에 처리되기 때문에 실행 순서가 보장되지 않을 수 있습니다. 비동기 방식은 작업이 완료되기를 기다리지 않고 다른 작업을 처리할 수 있으므로 시간을 효율적으로 활용할 수 있습니다.07. Iterator, Generator[ Symbol ]Symbol(심볼)은 ECMAScript 6에서 추가된 원시 데이터 타입입니다. 심볼은 고유하고 변경 불가능한 값으로, 주로 객체의 속성 키로 사용됩니다. 심볼은 Symbol() 함수를 통해 생성되며, 생성된 심볼은 다른 심볼과 절대로 동일하지 않습니다.심볼은 유일성과 불변성을 보장하기 때문에 객체의 속성 키로 사용하면 충돌을 방지하고 예상치 못한 동작을 방지할 수 있습니다. 또한, 심볼은 접근할 수 없는 외부에서 속성을 덮어쓸 위험을 줄여줍니다. 예를 들어, 두 개의 객체가 동일한 심볼을 속성 키로 사용하더라도 실제로는 서로 다른 속성을 가지게 됩니다.[ Iterator ]Iterator(이터레이터)는 JavaScript의 내장 프로토콜 중 하나로, 반복 가능한 객체(Iterable)에서 요소를 차례대로 가져오는 방법을 제공하는 인터페이스입니다. 이터레이터는 next() 메서드를 사용하여 다음 요소를 반환하고, { value, done } 형태의 객체로 값을 전달합니다. value에는 다음 요소의 값이 들어있고, done은 이터레이터가 더 이상 요소를 가지고 있지 않을 때 true가 됩니다. Symbol.iterator 메서드를 사용하여 이터레이터를 반환하는 것이 일반적입니다.[ Generator ]Generator(제너레이터)는 이터레이터를 생성하는 함수입니다. 제너레이터 함수는 function* 키워드로 정의되며, 함수 내부에서 yield 키워드를 사용하여 값을 반환하고 일시적으로 실행을 중지하며, next() 메서드를 호출함으로써 다시 시작할 수 있습니다. 제너레이터 함수를 호출하면 제너레이터 객체가 반환되며, 이 객체는 이터레이터 프로토콜을 따르기 때문에 next() 메서드를 사용하여 값을 가져올 수 있습니다. 08. Design Pattern디자인 패턴의 장점재사용성과 유지보수성 향상코드 가독성과 이해도 향상소프트웨어의 확장성과 유연성설계의 일관성과 표준화문제 해결 과정의 가속화Singleton Pattern: 특정 클래스의 인스턴스를 전역에서 하나만 생성하고 접근할 수 있도록 하는 디자인 패턴입니다.Factory Pattern: 객체를 생성하는 인터페이스를 정의하고, 이를 서브 클래스에서 구현하여 객체 생성을 캡슐화하는 디자인 패턴입니다.Mediator Pattern: 객체들 간의 상호작용을 중재하는 중재자 객체를 도입하여 결합도를 낮추고, 객체 간의 직접적인 통신을 제어하는 디자인 패턴입니다.Observer Pattern: 한 객체의 상태 변화에 따라 다른 객체들에게 자동으로 변화를 알리고, 그에 따른 처리를 할 수 있도록 하는 디자인 패턴입니다.Module Pattern: 모듈화된 코드를 구성하여 캡슐화하고, 필요한 부분만 노출시키며 코드의 재사용성과 유지보수성을 향상시키는 디자인 패턴입니다. 느낀점 및 각오자바스크립트의 중요성에 대하여 다시 한 번 크게 느낄 수 있었던 1주차였습니다. 자바스크립트 지식을 탄탄히 쌓고 배운 것들을 항상 이해하고 프로젝트에 적용까지 하며 기초를 상시 이해하며 면접준비까지 탄탄하게 해야겠다는 생각이 들었습니다. 남은 기간 동안에도 과제 수행을 열심히하며 저의 성장을 위해 최선을 다해보겠습니다! 감사합니다.

웹 개발FE워밍업클럽

이양구

[인프런 워밍업 클럽 FE 0기] 미션1 - 음식 메뉴 앱

🍝 food-recipe-app API from TheMealDBGitHub food-recipe-app 개요인프런 워밍업 클럽 FE 0기의 첫 번째 미션인 '음식 메뉴 앱' 만들기입니다.따라하며 배우는 자바스크립트의 섹션 1~3(자바스크립트 기초, Window 객체 및 DOM, Event)를 보고 자바스크립트의 DOM 요소를 조작하는 데 중점을 두었습니다.음식 데이터는 TheMealDB의 API를 이용했습니다. 사용한 API가 '음식 레시피'라서 이름을 변경했습니다. 목표문서 객체 모델(The Document Object Model, 이하 DOM)의 메소드(methods)를 이용해 요소(element)에 접근하고 생성하고 교체하기이벤트 리스너(Event Listener) 메소드를 이용해 요소에 이벤트를 등록하고 이벤트 객체 이용하기메뉴 데이터를 Fetch API를 사용해 불러오기 구현이벤트 위임(Event Delegation)을 이용한 이벤트 생성/* <nav id="food-navigation"> <div class="food-navigation-item"> <button id="Beef"> <figure> <img src="https://www.themealdb.com/images/category/beef.png"> <figcaption> Beef </figcaption> </figure> </button> </div> // ... </nav> */ // Not Event Delegation foodNavigation.querySelectorAll('button').forEach((button) => { button.addEventListener('click', async () => { const targetId = button.id; await setFoodList(targetId); }); }); // Event Delegation foodNavigation.addEventListener('click', async (event) => { const targetElement = event.target; // closest() 메서드는 주어진 CSS 선택자와 일치하는 요소를 찾을 때까지, // 자기 자신을 포함해 위쪽(부모 방향, 문서 루트까지)으로 문서 트리를 순회합니다. const targetDiv = targetElement.closest('.food-navigation-item'); if (!targetDiv) { return; } const targetButton = targetDiv.querySelector('button'); const targetId = targetButton.id; await setFoodList(targetId); });이벤트 위임이란 '상위 요소에서 하위 요소의 이벤트를 제어하는 것'을 의미합니다.이벤트를 위임하는 이유이벤트를 하나의 핸들러로 처리함으로써 메모리 사용량을 줄이고 성능을 향상시킬 수 있다.새로운 요소가 추가되거나 제거되는 경우 이벤트 리스너는 상위 요소에 연결되어 있어 재연결의 필요성이 줄어든다.저는 nav 태그에 이벤트를 등록하고 closest 메서드를 이용해 버튼의 id를 찾는 방법을 사용했습니다. 하위 요소를 제거하고 생성한 요소를 추가하기/* <div id="food-list"> <div class="food-list-item"> <figure> <img src="img src" /> </figure> <div class="food-list-item-desc"> <p>food name</p> <hr /> <div> food recipe </div> </div> </div> </div> */ const foodList = await getFoodList(strCategory); const foodListElement = document.getElementById('food-list'); const foodListItem = document.querySelectorAll('.food-list-item'); foodListItem.forEach((item) => item.remove()); // foodListElement.innerHTML = ''; foodList.map(async (food) => { // ... const foodElement = getFoodElement( idMeal, strMeal, strMealThumb, strInstructions ); foodListElement.appendChild(foodElement); });배웠던 removeChild()와 replaceChild() 메서드를 이용하고자 했으나...'만약 해당 카테고리의 음식 리스트의 개수가 다르다면 어떻게 하지?'라는 생각에 한번에 제거하기로 결정했습니다.처음엔 innerHTML을 이용해 하위 코드를 공백으로 만들었지만, 뭔가 이건 너무 이상하다는 생각(요소의 참조나 연결 같은 게 깨지진 않을까)이 들어 찾아보았습니다.stack overflow의 Remove child nodes (or elements) or set innerHTML=""?라는 글에서는 innerHTML은 하위 요소의 이벤트 핸들러가 완전히 제거되지 않을 수도 있다고 한다.또한 Why InnerHTML Is a Bad Idea and How to Avoid It?에서는 innerHTML이 보안상 좋지 않다는 점을 말하고 있다. Stack Overflow의 글을 자세히 읽어 보니 다음과 같은 글이 있었다.What is the best way to empty a node in JavaScript그리고 MDN 문서에도 이렇게 소개하고 있다.replaceChildren() provides a very convenient mechanism for emptying a node of all its children. You call it on the parent node without any argument specified:즉 replaceChildren()메서드를 빈 인자로 실행하면 하위 자식 노드를 모두 지워준다는 것...!😅 회고빈 폴더를 놓고 코드를 작성해본 게 너무 오랜만인 것 같다.자료를 찾기 귀찮다는 마음과 첫 미션이니까 API를 써볼까 하며 자만했던 순간도 있었다.미션의 목적보다 어느새 다른 부분을 신경 쓰느라 배보다 배꼽이 점점 커지는 것 같았다.딸랑 script 태그 한 줄 작성하고 js 파일을 제대로 못 불러와서 몇 시간을 해결 방법을 찾아서 해매기도 했다.😭이벤트 위임 코드를 작성할 때 이은재 님의 시나브로 자바스크립트에서 배웠던 부분을 참고했다.음식 레시피를 불러올 때 요소를 지우고 불러와서 그런지 해당 부분이 사라지고 나타나서 페이지가 늘었다 줄었다 하는 게 눈에 띈다.이래서 가상 돔을 쓰는걸까? 아니면 태그의 속성을 하나하나 수정하면 되는걸까?일단 진도를 따라잡고 배워서 발전시켜야겠다. DemoRecord by ScreenToGif

프론트엔드워밍업워밍업클럽FE프론트프론트엔드과제미션발자국

이슬

인프런 워밍업 클럽 스터디 1기 FE 과제(9번 과제)

[9번 과제(Day10) - 디즈니 플러스 앱]따라하며 배우는 리액트 A-Z학습 범위: Section 4 ~ 5https://github.com/helloleesul/inflearn-warmup-club-study/tree/main/disney-plus-app과제 이미지- 구글 로그인, swiper, 모달- 유튜브 플레이, 검색, 상세페이지, 로그아웃고민한 것MovieModal mount될 때에는 애니메이션이 되고, unmount될 때에는 애니메이션 없이 툭 사라져버린다.내가 원하는 것은 모달이 꺼질때에도 애니메이션으로 사라지게 한 후, unmount되게 하는 것기존 코드// Row 컴포넌트 modalOpen && <MovieModal {...movieSelection} setModalOpen={setModalOpen} />이전 코드에서는 modalOpen 상태가 참일 때만 MovieModal이 렌더링되었지만, 변경된 코드에서는 MovieModal 컴포넌트 내부에서 직접적으로 modalOpen 상태를 조작할 수 있도록 했다.변경 코드 // Row 컴포넌트 <MovieModal {...movieSelection} modalOpen={modalOpen} setModalOpen={setModalOpen} /> // MovieModal 컴포넌트 const MovieModal = ({ ... modalOpen, setModalOpen, }) => { const ref = useRef(); const [showModal, setShowModal] = useState(false); const [animation, setAnimation] = useState(""); useOnClickOutside(ref, () => { // 2. 닫기 이벤트 setModalOpen(false); }); useEffect(() => { if (!modalOpen) { // 4. 사라지는 애니메이션으로 설정 setAnimation("animate-fade-down animate-reverse"); } else { setShowModal(true); setTimeout(() => { // 1. 나타나는 애니메이션으로 설정 setAnimation("animate-fade-up"); }, 10); } }, [modalOpen]); return ( showModal && ( <div className="animate-fade"> <article className={`${animation}`} ref={ref} onAnimationEnd={() => { // 3. 사라지는 애니메이션 끝이나면 showModal false if (!modalOpen) setShowModal(false); }} ></article> </div> ) ) };MovieModal 컴포넌트 내부에서는 modalOpen 상태에 따라 모달의 나타남과 사라짐을 제어하는 useEffect와 useState를 사용했다.modalOpen 상태가 변경될 때마다 useEffect가 실행되어 애니메이션 클래스를 설정하고, 애니메이션 효과를 주는 CSS 클래스를 적용시킨다.onAnimationEnd 이벤트 핸들러를 사용하여 애니메이션 종료 후에 showModal 상태를 변경하여 모달이 화면에서 완전히 사라지도록 처리했다. 추가한 것tailwind css + 스크롤바 숨기는 플러그인, 애니메이션 플러그인/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./src/**/*.{js,jsx,ts,tsx}"], theme: { extend: {}, }, plugins: [ require("tailwind-scrollbar-hide"), require("tailwindcss-animated"), ], };회원만 접근 가능한 parent 컴포넌트 생성const UserGuard = () => { const navigate = useNavigate(); useEffect(() => { const profilePictureUrl = localStorage.getItem("profilePictureUrl"); // 로그인 시 저장한 구글 유저 프로필이미지가 없다면 LandingPage로 이동 if (!profilePictureUrl) { navigate("/"); } }, [navigate]); return <Outlet />; }; export default UserGuard; function App() { return ( <div className="App"> <Routes> <Route path="/" element={<Layout />}> <Route index element={<LandingPage />} /> 👇 <Route element={<UserGuard />}> <Route path="main" element={<MainPage />} /> <Route path=":movieId" element={<DetailPage />} /> <Route path="search" element={<SearchPage />} /> </Route> </Route> </Routes> </div> ); } export default App;로그인 상태일 때 랜딩 페이지 접근 불가const LandingPage = () => { const navigate = useNavigate(); useEffect(() => { const profilePictureUrl = localStorage.getItem("profilePictureUrl"); // 로그인 시 저장한 구글 유저 프로필이미지가 있다면 MainPage로 이동 if (profilePictureUrl) { navigate("/main"); } }, [navigate]); return (...) };

웹 개발인프런워밍업클럽FE1기과제

임하스

[인프런 워밍업 스터디 클럽 1기] FE 2주차 발자국

시작강의가 좀 밀려있긴 한데... 기간 안엔 다 들을 수 있을 거라 생각하며 차근차근 정리 중이다...정리자바스크립트 this 키워드메서드 내 this ⇒ 해당 객체(Object)함수 내 this ⇒ window 객체생성자 함수 내 this ⇒ 빈 객체(해당 인스턴스)forEach문 내 this ⇒ 두 번째 매개 변수 값, 없을 경우 window 객체 또는 해당 객체화살표 함수 내 this ⇒ 상위 스코프의 this예제메서드 내 ⇒ 해당 객체(Object)const audio = { title: 'a', play() { console.log('play this', this); }, }; audio.stop = function () { console.log('stop this', this); }; audio.play(); //play this { title: 'a', play: f } audio.stop(); //play this { title: 'a', play: f } 함수 내 ⇒ window 객체function playAudio() { console.log(this); } playAudio(); // Window 객체 생성자 함수 내 ⇒ 빈 객체(해당 인스턴스)function Audio(title) { this.title = title; // 이게 없으면 빈 객체 console.log(this); } const audio = new Audio('a'); // Audio { title: 'a' } forEach문 내 ⇒ 두 번째 매개 변수 값, 없을 경우 window 객체 또는 해당 객체const audio = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach(function (category) { console.log(`title: ${this.title}, category: ${category}`); // 함수 내에 있기 때문에 window 객체를 가리킴 }); }, }; audio.displayCategories(); const audio2 = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach( function (category) { console.log(`title: ${this.title}, category: ${category}`); }, { title: 'audio' } ); // forEach 두 번째 매개변수에 있는 값을 참조시킬 수 있음 }, }; audio2.displayCategories(); const audio3 = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach( function (category) { console.log(`title: ${this.title}, category: ${category}`); }, this // audio3 해당 객체를 참조 ); }, }; audio3.displayCategories(); 화살표 함수 내 ⇒ 상위 스코프의 thisconst audio = { title: 'audio', categories: ['rock', 'pop', 'hiphop'], displayCategories() { this.categories.forEach(function (category) { console.log(`title: ${this.title}, category: ${category}`); // 상위 스코프의 this(Lexical this) => audio { title : 'audio', categories: Array(3) } }); }, }; audio.displayCategories(); call, apply, bind함수에서 this 사용 시, window 객체가 아닌 값을 참조시키기 위해 사용함수명.call(thisArg[, arg1, arg2, ...]) : 함수를 호출하는 함수함수를 호출할 때, 첫 번째 매개 변수에 함수 내에서 사용할 this 값을 지정할 수 있음첫 번째 매개 변수로 null 또는 undefined가 전달되면 전역 객체가 this로 사용됨두 번째 ~ … 매개 변수로 호출할 함수에 전달될 인수 추가 가능const fullName = function (arg1, arg2) { console.log(`${this.firstName} ${this.lastName}`); console.log(`${arg1} ${arg2}`); }; const person = { firstName: 'Im', lastName: 'Hass', }; fullName.call(person, '매개변수1', '매개변수2'); // Im Hass // 매개변수1 매개변수2 함수명.apply(thisArg[, argArray]) : call()과 동일call()과 인수를 배열로 넣어주는 차이const fullName = function (arg1, arg2) { console.log(`${this.firstName} ${this.lastName}`); console.log(`${arg1} ${arg2}`); }; const person = { firstName: 'Im', lastName: 'Hass', }; fullName.apply(person, ['매개변수1', '매개변수2']); **함수명.bind(thisArg)**: 함수가 실행되지 않고 바인딩만 시켜줌함수를 바인딩할 때, 새로운 함수를 생성함 → 원본 함수와 동일한 코드를 가지지만 this가 바인딩된 값이 들어감 ⇒ 새로운 변수에 담거나 즉시 실행 시켜서 사용해야 함function func(language) { if (language === 'kor') { console.log(`language: ${this.korGreeting}`); } else { console.log(`language: ${this.engGreeting}`); } } const greeting = { korGreeting: '안녕', engGreeting: 'Hello', }; // 원본은 변하지 않기 때문에 undefined 출력 // func.bind(greeting); // func('kor'); // language: undefined // 1. 새 변수에 담아서 사용하기 // const boundFunc = func.bind(greeting); // boundFunc('kor'); // language: 안녕 // 2. 즉시 실행하기 func.bind(greeting)('kor'); Event Loop자바스크립트는 동기 언어지만, 다른 것의 도움(브라우저의 자바스크립트 엔진 또는 Node.js와 같은 런타임 환경)을 통해 비동기로 처리할 수 있음.ex) setTimeout(브라우저 api-window object 또는 node api-global object)setTimeout에 설정된 값이 0(즉시 실행)이어도 콜백 큐에서 호출 스택이 비어야 들어가기 때문에 순서가 다르게 출력됨자바스크립트 코드를 실행하기 위해 엔진이 필요한데, 자바스크립트 엔진에는 메모리 힙, 콜 스택 두 가지 주요 구성 요소가 있음Call Stack(호출 스택)코드 실행에 사용되는 메모리 구조함수 호출 및 반환을 추적함코드가 실행될 때 함수 호출은 스택에 추가되고, 함수가 완료되면 스택에서 제거됨Callback Queue(콜백 큐)비동기 작업의 콜백 함수들이 대기하는 대기열비동기 작업이 완료되면 해당 콜백 함수가 콜백 큐에 추가됨Event Loop(이벤트 루프)콜 스택과 콜백 큐를 계속 모니터링하여 콜 스택이 비어있을 때 콜백 큐의 첫 번째 콜백을 콜 스택으로 이동시킴내부 진행 참고 사이트 : https://kamronbekshodmonov.github.io/JELoop-Visualizer/Closure외부 함수 보다 생명 주기가 긴 내부 함수 중에서, 종료된 외부 함수의 값을 참조할 수 있는 내부 함수를 의미한다.클로저 사용 전(오류: b에 접근 불가)let a = 'a'; function funcB() { let c = 'c'; console.log(a, b, c); } function funcA() { let b = 'b'; console.log(a, b); funcB(); } funcA(); // a b // ReferenceError: b is not defined 클로저 사용 후(해결: 내부 함수로 변경)let a = 'a'; function funcA() { let b = 'b'; console.log(a, b); function funcB() { let c = 'c'; console.log(a, b, c); } funcB(); } funcA(); // a b // a b c 구조 분해 할당배열이나 객체의 속성을 해체하여 개별 변수에 담을 수 있게 하는 Javascript 표현식객체 구조 분해 할당let person = { name: 'hass', age: 30, phone: '123', address: { zipcode: 1234, street: 'rainbow', number: 42, }, }; let { address: { zipcode, street, number } } = person; console.log(zipcode, street, number); 별명 지정let people = [ { name: 'mike', age: 35, family: { mother: 'Jane', father: 'Harry', sister: 'samantha', }, }, { name: 'Tom', age: 25, family: { mother: 'Norah', father: 'Richard', brother: 'Howard', }, }, ]; for (let { name: n, family: { father: f }, } of people) { console.log(`Name : ${n}, Father: ${f}`); } // Name : mike, Father: Harry // Name : Tom, Father: Richard Map() 메서드배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환함array.map(callback(currVal[, index[, array]]])[, thisArg])첫 번째 매개 변수로 callback 함수가 들어가고, 두 번째 매개 변수에 this 바인딩 가능Filter() 메서드주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환함array.filter(callback(element[, index[, array]])[, thisArg])첫 번째 매개 변수로 callback 함수가 들어가고, 두 번째 매개 변수에 this 바인딩 가능Reduce() 메서드배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환함arr.reduce(reducerFunction(acc, curr, idx, src), initalValue)reducerFunction 4개의 인자를 가짐누산기(acc)현재 값(curr)현재 인덱스(idx)원본 배열(src)예제const result = [0, 1, 2, 3, 4].reduce(function (acc, curr, idx, src) { return acc + curr; }, 10); // console.log(result); // 20 acc curr idx src return value 1번째 호출 10 0 0 [0, 1, 2, 3, 4] 10 2번째 호출 10 1 1 [0, 1, 2, 3, 4] 11 3번째 호출 11 2 2 [0, 1, 2, 3, 4] 13 4번째 호출 13 3 3 [0, 1, 2, 3, 4] 16 5번째 호출 16 4 4 [0, 1, 2, 3, 4] 20  undefiend vs null공통점둘 다 원시 자료형undefiend의 타입은 undefiend, null 타입은 null(object로 나옴, strit 모드는 null) 유일한 타입undefined아무 값도 할당받지 않은 상태개발자의 의도 X, 자바스크립트 엔진이 초기화한 값null비어있는 값, 존재하지 않는 값개발자가 의도한 값null을 할당하면 변수가 이전에 참조하던 값을 명시적으로 참조하지 않겠다고 하는 것이므로, 자바스크립트 엔진이 변수의 메모리 공간에 대해 가비지 콜렉션을 수행함얕은 비교(Shallow Compare)숫자, 문자 등 원시 자료형은 값으로 비교배열, 객체 등 참조 자료형은 참조되는 위치를 비교IdentifiersuserName seasons isFinished enemies userCall StackAddress Value 0012ASF “Hass” 3241AF 5 416UHDI true 1235JFT HHSYDW1462 JFFI12HA KHS18205JAHeapAddress Value HHSYDW1462 ["ene1", "ene2", "ene3"] KHS18205JA {name: "hass", profession: "Drug dealer"}깊은 비교(Deep Compare)객체의 경우에도 값으로 비교Object Depth가 깊은 경우 : lodash 라이브러리의 isEqual() 사용Obejct Depth가 깊지 않은 경우: JSON.stringfy() 사용const obj1 = { a: 'a', b: 'b' }; const obj2 = { a: 'a', b: 'b' }; console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true 얕은 복사, 얕은 동결얕은 복사 : 내부에 중첩된 값은 복사되지 않음(기존과 동일한 객체를 참조함, 하나를 변경하면 똑같이 변경됨)얕은 동결 : Object.freeze() 메서드, 객체를 동결하여 변경할 수 없도록 함.얕은 복사를 하는 연산자 : 전개 연산자, Oject.assign(), Array.from(), slice()깊은 복사내부 중첩된 값까지 복사함JSON.parse(JSON.stringify(object)) 또는 중첩된 부분에 전개 연산자 사용 { …aObj, cObj: {…aObj.cObj }}lodash 라이브러리를 활용structuredClone(object) 메서드 활용함수 선언문, 함수 표현식함수 표현식 : 함수를 만들고 변수에 할당, 익명 함수호이스팅 시, 함수 선언식만 영향을 받음함수 표현식은 인터프리터가 해당 코드 줄에 도달할 때만 로드됨함수 표현식은 정의된 로컬 변수의 복사본을 유지할 수 있도록 호이스팅 되지 않음즉시 호출(실행) 함수 표현식(IIFE(Immediately Invoked Function Expression))정의 하자마자 즉시 실행되는 함수를 의미함기본 형태 ( function () {} )()첫 번째 소괄호( ... ) : 전역 선언을 막고, IIFE 내부로 다른 변수의 접근을 막음두 번째 소괄호 …() : 즉시 실행 함수를 생성하는 괄호, 이를 통해 Javascript 엔진이 함수를 즉시 해석하고 실행함IIFE를 변수에 할당하면 IIFE 자체는 저장되지 않고, 함수가 실행된 결과만 저장할 수 있음IIFE로 Closure가 가능하게 만든 후, 내부 함수에서 외부 함수에 있는 변수에 접근되게 함사용 목적변수를 전역으로 선언하는 것을 피하기 위해IIFE 내부에 다른 변수들이 접근하는 것을 막기 위해익명 함수를 만들기 위해 (이름이 없기 위한 두 가지 조건 중 한 가지)이 함수를 할당 받을 변수를 지정해야 함이 함수를 즉시 호출해야 함// 이름은 선택 사항이라고 했지만 function 키워드로 만들 때 에러 발생 // function (a, b) { // Error: Identifier expected. // return a- b; // } // 조건1 const minus = function (a, b) { return a - b; }; // 조건2 (function (a, b) { return a - b; })(1, 2); IIFE 적용 전const score = () => { let count = 0; return { current: () => { return count; }, increment: () => { count++; }, reset: () => { count = 0; }, }; }; console.log(score); // () => {...} 함수 출력 console.log(score().current()); // 0 score.increment(); // count를 1 증가 시킴 console.log(score().current()); // 0 => score()를 다시 실행 시켜서 count가 0으로 초기화됐기 때문에 값이 증가하지 않음 IIFE 적용 후const score = (() => { let count = 0; return { current: () => { return count; }, increment: () => { count++; }, reset: () => { count = 0; }, }; })(); console.log(score); // { current: f, ... } console.log(score.current()); // 0 score.increment(); // count를 1 증가 시킴 console.log(score.current()); // 1 Intersection observer무한 스크롤, 이미지 lazy loading 등을 구현할 때 사용됨Lazy Loading 예제<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Intersection Observer</title> <style> img { width: 400px; height: 300px; display: block; margin: 10px auto; } </style> </head> <body> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <img src="<https://via.placeholder.com/400x300>" alt="placeholder image" data-src="<https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300>"> <script> const observer = new IntersectionObserver( function (entries, observer) { console.log('entries', entries); entries.forEach((entry) => { if (entry.isIntersecting) { // 루트 요소와 타겟 요소가 교차하면 console.log('옵저버'); entry.target.src = entry.target.dataset.src; // dataset에 있는 url을 src에 넣어줌 observer.unobserve(entry.target); // Lazy loading 후에는 observer 해제 } }); }, { threshold: 1 } // 타겟 요소가 루트 요소와 100% 겹치면 콜백 함수 실행 ); const imgEls = document.querySelectorAll('img'); console.log(imgEls); imgEls.forEach((img) => { observer.observe(img); }); </script> </body> </html> 순수 함수(Pure Function)함수형 프로그래밍 패러다임의 한 부분특정 함수가 다른 함수에 미치는 예기치 못한 영향을 최소화하고, 함수를 만들고 실행할 때 어떤 결과값을 리턴할지 예측할 수 있다는 장점이 있음가능한 순수 함수를 사용하는 것이 좋음클린 코드를 위해서테스트를 쉽게 하기 위해서디버그를 쉽게하기 위해서독립적인 코드를 위해서두 가지 규칙이 있음같은 입력이 주어졌을 때, 언제나 같은 결과값을 리턴한다 (same input → same output)사이드 이펙트를 만들지 않는다 (동일한 input이지만 외부 값에 의해 변경되는 output → impure하게 만듦)커링(Curry Function)함수와 함께 사용하는 고급 기술 (다른 언어에도 존재하는 기술)f(a, b, c) 처럼 단일 호출로 처리하는 함수를 → f(a)(b)(c)와 같이 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합될 수 있게 변환하는 것즉, 함수를 호출하는 것이 아닌 변환하는 것예제const sum = (x, y) => x + y; console.log(sum(10, 20)); // 30 const curriedSum = (x) => (y) => x + y; console.log(curriedSum(10)); // f() y => x + y console.log(curriedSum(10)(20)); // 30 const makeFood = (ingredient1) => { return (ingredient2) => { return (ingredient3) => { return `${ingredient1} ${ingredient2} ${ingredient3}`; }; }; }; // const hamburger = makeFood('Bread')('Ham'); // console.log(hamburger); // (ingredient3) => {...} 완성되지 않음 const hamburger = makeFood('Bread')('Ham')('Tomato'); console.log(hamburger); // Bread Ham Tomato => 3개의 인수를 다 넣어줘야 완성됨 // makeFood 축약 const cleanMakeFood = (ingredient1) => (ingredient2) => (ingredient3) => `${ingredient1} ${ingredient2} ${ingredient3}`; const newHamburger = cleanMakeFood('Bread')('Ham')('Tomato'); console.log(newHamburger); // Bread Ham Tomato // 일반 log 함수 function log(date, importance, message) { alert(`[${date.getHours()} : ${date.getMinutes()}]: [${importance} ${message}]`); } log(new Date(), 'DEBUG', 'same bug'); // 커리 함수 function curry(f) { return function (a) { return function (b) { return function (c) { return f(a, b, c); }; }; }; } const curriedLog = curry(log); curriedLog(new Date())('DEBUG')('same bug'); // 동적으로 추가되는 커리 함수 (매개 변수 개수 상관 없이 커리 함수로 변경 가능) function curry2(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this.args); } else { return function (...args2) { return curried.apply(this, args.concat(args2)); }; } }; } // const sum = (x, y, z) => x + y + z; const sum = (x, y, z, j) => x + y + z + j; const curriedSum = curry2(sum); // console.log(curriedSum(1)(2)(3)); console.log(curriedSum(1)(2)(3)(4)); strict modeJavascript의 엄격 모드제한된 버전을 선택하여 암묵적인 “느슨한 모드(sloppy mode)”를 해제하기 위한 방법시멘틱스에 몇 가지 변경이 일어남기존에는 무시되던 에러를 throwingJavascript 엔진의 최적화 작업을 어렵게 만드는 실수를 바로 잡음. 가끔 엄격 모드의 코드가 비엄격 모드와 동일한 코드 보다 더 빠르게 작동하도록 만들어줌.적용 방법파일에 "use strict" 지시자 입력함수 안에 "use strict" 지시자 입력하여 해당 함수만 적용 가능class를 사용하면 자동으로 strict 모드 적용자바스크립트 파일의 타입을 module로 설정하면 strict 모드 적용<script src="script.js" type="module">기존과 차이점할당할 수 없는 값에 할당하려 할 때 ex) 선언되지 않은 변수, undefiend, NaN수정할 수 없는 값을 수정하려 할 때 ex) readOnly 객체동일한 이름의 파라미터가 존재할 때함수 내의 this가 undefiend가 됨

프론트엔드인프런워밍업스터디클럽FE1기발자국