블로그

코드캠프

입문자도 가넝한 8주만에 개발자 되는 법

안녕하세요! 실무 코딩부트캠프, 코드캠프입니다 :)날씨가 조금씩 따뜻해지면서 식곤증이 부쩍 늘어나고 있지 않나요?(전지적 컴퓨터 시점)그래서 코드캠프가 눈이 번쩍! 뜨이는(👀) 강의 업데이트 소식을 준비해왔어요!특히 비전공이지만 개발 커리어에 관심이 있는 분들이라면, 주목해주셔도 좋아요 :)코딩을 몰라도(NEW!) 2개월만에 개발자가 되는 '관리형' 코딩 부트캠프인프런에 입성한지 얼마 되지 않았지만 뜨거운 관심과 애정 어린 피드백을 받으며, 더 나은 컨텐츠를 제공하기 위해 코드캠프도 열심히 성장 중인데요. (정말 감사합니다)그 중 사전 기초 지식 없이는 부트캠프 합류가 어려워 신청을 망설였던 분들을 많이 보았어요.그래서 상담 후 등록을 완료해주신 모든 분들께,[시작은 프리캠프], [강력한 CSS], [훈훈한 Javascript] 기초 강의를 무료로 제공합니다!사전 지식을 필요로 하시는 분들은 웹 개발의 기초를 습득하시고사전 지식이 이미 있으신 분들은 기초 내용을 복습하면서 튼튼하게 다져보세요 :)⚠️ 단, 기초 강의 수강 기간은 부트캠프 기간(8주)에 포함되지 않으므로 등록하신 기수의 개강 일정에 맞춰 수강해 주셔야 합니다!(MBTI가 P인 분들)개강 전 기초 강의 수강 계획을 짜기 힘드신 분들은 언제든 코드캠프에 문의해주세요!개강일에 맞춰 수강할 수 있도록 체계적인 시간표를 제공해드리겠습니다 😊👉 2기 일정 : 03.06 - 04.28 (선착순 모집) ▶ 20% 할인 중!👉 3기 일정 : 04.03 - 05.29 (선착순 모집) ▶ 20% 할인 중!

웹 개발웹개발프론트엔드백엔드부트캠프비전공자JavascriptNode.jsReactNext.jsNest.js

Inje Lee (소플)

프론트엔드 지식 포털 소개 및 리덕스 문서 업데이트

안녕하세요, 소플입니다 😀여러분들에게 도움이 될 만한 몇가지 새소식을 전달드립니다!프론트엔드 지식 포털 소개최근에 제가 프론트엔드 지식 포털 사이트를 만들었습니다.사이트 이름은 FrontOverflow이고 주소는 아래와 같습니다.https://www.frontoverflow.com/사이트를 간단하게 소개하면 'Front-end 계의 StackOverflow' 라고 생각하시면 됩니다.한국어를 기본 언어로 하며, 프론트엔드와 관련된 질문과 답변을 자유롭게 할 수 있습니다.초반에 올라오는 모든 질문들에 대해서는 제가 직접 답변을 달아드릴 예정이니,개발하면서 막히거나 어려운 부분이 있다면 주저하지말고 편하게 질문 올려주세요!(프론트엔드 아키텍처, 컴포넌트 설계 등의 질문도 환영합니다 😀)리덕스 문서 업데이트그리고 최근에 출시한 처음 만난 리덕스 강의와 관련해서,실습 파트를 제외한 모든 강의 내용을 담은 문서를 FrontOverflow에 정리해두었습니다.강의를 수강한 분들은 복습하는 용도로 사용하시면 되고,수강하지 않으신 분들도 개인적으로 리덕스를 학습하는 용도로 사용하시면 분명 도움이 될 겁니다.강의 문서는 아래 링크에서 확인이 가능합니다.🔗 처음 만난 리덕스 강의 문서 보기 모든 수강생 분들의 성장을 응원합니다! 🎉감사합니다.

프론트엔드리액트리덕스프론트엔드ReactReduxFrontend

주니어 프론트엔드 개발자에게 꼭 필요한 역량은?

주니어 프론트엔드 개발자에게 핵심이 되는 기술은 무엇일까요? 오늘날 프론트엔드 개발 프레임워크/라이브러리 중 가장 많이 쓰이는 리액트(React)의 경우에도 막상 ‘제대로’ 쓰는 개발자는 흔하지 않다고들 하죠.현대 웹 개발은 복잡한 요구사항과 여러 플랫폼에서의 동작을 고려해야 해요 때문에 모던 자바스크립트(JavaScript) ES6+ 및 타입스크립트(TypeScript)에 대한 깊이있는 이해는 코드의 유지보수성과 확장성을 높이고 안정적인 웹 앱을 만들 수 있게끔 합니다. 아울러 커스텀 훅(Custom Hook), 재사용성 높은 컴포넌트(Component) 구현 등 리액트를 높은 수준으로 활용할 수 있다면 더 나은 사용자 경험과 개발 생산성을 도모할 수 있어요.더욱이 리덕스(Redux)와 같은 상태 관리 라이브러리 경험이 있다면 애플리케이션의 확장성을 향상시킬 수 있습니다. 덧붙여 FE 개발에서 필수적인 환경을 제공하는 Node.js를 이해하면 빌드 도구, 패키지 매니저, 테스팅 도구 등을 자동화하여 개발 프로세스를 효율화하고 더 높은 품질의 소프트웨어를 개발할 수 있겠죠.이렇듯 프론트엔드 개발자에게 중요한 기술은 여러 가지가 있겠지만, 중요한 건 알고 있는 기술의 가짓수가 아니라 그 깊이와 수준에 달려 있는데요. 다양한 요소들을 그저 사용해 본 수준이 아니라, 원리를 정확하게 이해하고 실무에서 응용할 수 있는지가 주니어 프론트엔드 개발자의 경쟁력으로 여겨지고 있습니다.•••주니어 프론트엔드 개발자에게 꼭 필요한 역량을 갖추고 싶다면?지금 인프런 프리즘 [프로가 되는 프론트엔드 개발자 로드맵 with React]을 통해 학습해보세요. https://www.inflearn.com/roadmaps/716•••인프런 프리즘 브랜드 스토리 읽어보기 >>

프론트엔드리액트React프론트엔드리덕스Redux상태관리TypeScript타입스크립트JavaScript자바스크립트

바다다다

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

수강강의1: 따라하며 배우는 자바스크립트 A-Z수강강의2: 따라하며 배우는 리액트 A-Z[19버전 반영]Tip: 워밍업 스터디 클럽을 참여하게 되면 [할인쿠폰]으로 강의를 할인 된 가격에 수강할 수 있다! 3주차 강의 수강3주차를 되돌아 보며..이번주 강의는 전반적으로 React와 친해지는(?) 시간을 가지게 된거 같다.React로 실제 화면을 구현해보고 각종 함수와 툴들을 사용해서 개발의 편의성을 높이는 것을 체감할 수 있었다.특히 주어진 미션과제를 수행하면서 React의 동작성, 상태관리에 대해서 더 깊게 알 수 있을 시간이 되었다.사실 이번주는 강의를 많이 듣지 못했다ㅠ 현업과 병행하다 보니 퇴근하고 강의 듣는다는 게 쉬운 일은 아니다. 특히 이번주는 시험검증 기간이라 이슈가 조금 많이 발생해서 야근도 잦고 그랬다... (모든 직장인, 학생 분들 화이팅입니다ㅠ 열심히 살아가봐요!) 그리고... 미션과제도 밀려서 주말에 몰아서 구현을 했다.미션과제를 수행하면서 중간중간 필요한 지식들은 강의를 들으면서 기록하며 학습을 진행했다.Javascript 강의 때와는 다르게 React 는 실습위주 이다보니 강의 내용을 정리한게 많지는 않다. (강의보면서 코딩따라 해보며 기능을 확인하고 미션에 적용하려고 했다)강의 내용을 다시 정리해보며..(주로 개인적으로 새롭게 알게된 것, 다시 기억해야하는 것들 위주로 정리했다)리액트는 프레임워크가 아니라 라이브러리React는 라이브러리, Vue, Angular는 프레임워크왜 라이브러리? 리액트는 전적으로 UI를 렌더링하는 것에 관여하기 때문상태관리, 라우팅, 테스트 등등을 위해 다른 라이브러리가 추가적으로 필요함 (vue, angular는 이런 것들이 이미 포함되어 있음)프레임워크: 어떠한 앱을 만들기위해 필요한 대부분의 것을 가지고 있는 것라이브러리: 어떠한 특정 기능을 모듈화 해놓은 것프레임워크는 라이브러리의 집합리액트 컴포넌트 - Component리액트 앱을 이루는 최소한의 단위, 여러 컴포넌트를 조합하여 하나의 페이지가 완성되는 것클래스형 컴포너트 - class component함수형 컴포넌트 - funcional component브라우저가 그려지는 원리와 가상돔리액트의 주요 특징 중 하나가 가상돔을 사용하는 것이다웹 페이지 빌드 과정 (CRP) - Critical Render Pathbrowser가 서버에서 페이지에 대한 HTML문서를 응답으로 받고, 해당 문서를 읽는다. 그 후 스타일을 입히고 뷰포트에 표시하게 된다DOM tree생성 → Render Tree생성(화면에 표시되는 모든 노드의 콘텐츠 및 스타일 정보를 포함) → Layout(reflow) → Paint(화면에 그리기)화면에서 DOM에 변화가 생기면 Render Tree부터 다시 랜더링 해야한다는 문제점!! → 비효율적→ 가상돔: 실제 DOM을 메모리에 복사해준 것데이터가 바뀌면 가상돔에 랜더링되고 이전에 생긴 가상돔과 비교해서 바뀐 부분만 실제 돔에 적용 시킴. 바뀐 부분을 찾는 과정을 diffing이라고 부르고, 바뀐 부분만 실제 돔에 적용시켜주는 것을 재조정(reconciliation)이라 한다.리액트 구조 살펴보기이름이 수정되면 안되는 파일public/index.html → page템플릿src/index.js → 자바스크립트의 시작점SPA는 어떻게 가능하게 되나?HTML5에서 History API를 사용해서 가능하게 함. (실제 react-dom에서 사용하는 방식)JSXJavascript syntax extension자바스크립트의 확장 문법이다React에서 의무적으로 사용하는 것 XJSX는 babel을 통해 변환된다컴포넌트에 여러 엘리먼트 요소가 있다면 반드시 부모요소 하나로 감싸줘야한다.JSX Keykey는 리액트가 변경, 추가 또는 제거된 항목을 식별하는데 도움이 된다. 요소에 안정적인 ID를 부여하려면 배열 내부의 요소에 키를 제공해야 한다 (key값을 기준으로 바뀐 가상돔을 감지한다)key에는 unique한 값을 넣어주자. index는 비추!React State컴포넌트의 렌더링 결과물에 영향을 주는 데이터를 갖고있는 객체이다. state가 변경되면 컴포넌트는 리랜더링(re-rendering)된다. 또한 state는 컴포넌트 안에서 관리된다.React HooksReactConf 2018에서 발표된 class없이 state를 사용할 수 있는 새로운 기능리액트의 생명주기react hooks를 통해 함수형 컴포넌트에서도 생명주기를 사용할 수 있게 데이터를 가져와서 컴포넌트 시작하자 마자 API호출하고 많은 부분을 사용할 수 있게되었다→ 코드가 간결해짐 (useEffect를 통해 componentDidMount, componentDidUpdate, componentWillUnmount를 다 수행해줌)HOC(higher order component)를 Custom react hooks로 대체해서 너무 많은 wrapper컴포넌트를 줄이게 된다.→ HOC: 화면에서 재사용 가능한 로직만을 분리해서 component를 만들고, 재사용 불가능한 UI와 같은 다른 부분들은 parameter로 받아서 처리하는 방식 (wrapper가 많아지면 데이터의 흐름을 파악하기 어려워짐)State, Propsstate해당 컴포넌트 내부에서 데이터를 전달할 때state는 mutable 변경가능하다state 이 변하면 re-render된다props상속하는 부모컴포넌트에서 자식 컴포넌트로 전달한다읽기전용으로 작년 컴포넌트 입장에서는 변하지 않는다. (변하게 하고자 하면 부모 컴포넌트에서 state를 변겨해줘야한다)구조 분해 할당(Destructuring)배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JS표현식클린코드를 위해 객체, 배열에 대해 구조 분해 할당Redux상태 관리 라이브러리redux데이터는 하나의 방향으로만 흐른다flow: react component에서 어떠한 이벤트가 발생하면 → Action 객체(어떤 작업을 수행할 것인지 description을 담고 있음)를 통해 reducer함수에게 액션을 발생하라고 한다.(dispatch) → reducer함수에서 로직 처리 → store내부 상태 업데이트: redux store state update → 새롭게 업데이트 된 상태를 이용해서 다시 렌더링 → component re-renderingRedux Tookitredux로직을 작성하기 위한 공식 권장 접근 방식 강의 내용을 들으면서 아쉬웠던 점..앞서 언급한 대로 이번주에는 강의보다는 실습 과제에 조금 더 초점을 두었다. 미션을 수행하면서 모르는 부분을 강의에 가서 찾아 듣고 다시 구현하고 하는 식으로 학습을 진행했다.React는 이론 보다는 실제 사용하고 구현해보는게 더 빠르게 성장하는거 같다. 특히 상태관리, 페이지 라우터 등 강의에서도 알려주시지만, 미션과제를 수행하면서 직접 기능을 동작시킬 수 있게 구현하는게 더 깊이 이해할 수 있었던 거 같다.아쉬운점은ㅠ 강의를 많이 못 들었다는거? 수료식까지 아직 시간이 조금 남았으니까 남은 강의도 열심히 들어보겠다.3주차 미션 완료React미션 두 개(포켓몬, 쇼핑몰)를 토요일 벼락치기로 수행했다.간단하다고 생각했는데 React가 아직 익숙하지 않아 중간중간 오류가 많이 발생했었고, 특히 프론트엔드는 화면이 바로 눈에 보이다 보니까 CSS 스타일 적으로 내맘대로 적용되지 않아서 화가났다(?). 그래도 tailwindcss에서 만들어진 component, icon들을 활용해서 미션을 수행완료할 수 있었다.React - Mission3: 포켓몬 도감 앱 만들기Demo: https://pokemon-mission-3.netlify.app/Source Code: https://github.com/rim0703/React-study/tree/react/mission3/React/3-pokemonpokeAPI 라는 오픈소스 API를 제공하는 곳이 있어서 외부API를 호출해서 구현했다API문서가 얼마나 중요한 역할을 하는지 이번 미션에서 깊이 깨달았다.. ㄷㄷ(백엔드 화이팅)(지연로딩 적용) 사용자의 화면 로딩 속도를 고려하면 첫 화면에 20개씩 불러오도록 했고, 스크롤이 하단에 도달하면 추가로 호출하도록 구현했다.(Trick) 시연화면에서 로딩이 출력되는데 사실 화면이 너무 빨리 렌더링 완료돼서 로딩이 보이지 않아 로딩 동글뱅이를 보기 위해 setTimeout으로 1초 지연을 넣었다 ^^;;(검색창) 조금 버그가 있다. 페이지를 영문이 아닌 한글로 만들다보니 검색도 한글로 하도록 했는데 상태를 상위-하위 컴포넌트 간에서 전달하다 보니 조금 어려웠다. 또한 API문서 기본은 영문으로 제공해서 영문과 한글을 매칭시키는 별도의 작업도 필요했다. redux를 사용해서 검색창을 상단 navigation바에 위치해보려고 한다.  React - Mission6: 리덕스를 이용한 쇼핑몰(버거몰) 앱 만들기Demo: https://burger-mall-mission-6.netlify.app/Source Code: https://github.com/rim0703/React-study/tree/react/mission6/React/6-shopping-mall1주차 미션 내용에서 언급했듯이.. JS강의에서 만들었던 메뉴 화면을 그대로 따와서(CSS공부가 아니니까^^) 쇼핑몰과 비슷한 기능들이 있는 버거몰을 만들어보았다.(컴포넌트화) HTML,JS,CSS로 구현된 내용을 React로 옮겼고, 원래 파일 1개씩 정의된 내용을 React Component로 분류하였다.(장바구니-localstorage) DB를 별도로 연동된게 아니여서 localstorage를 활용해서 장바구니를 구현하였다.(Redux) 리덕스 강의 내용에 기반한 프로젝트이지만 Redux강의를 듣고 이해가 잘되지 않아서 우선 기본 방식으로 앱을 구현하였다. Redux공부를 조금 더 하고 리팩토링을 진행할 예정이다. 마지막으로 3주차 회고이제 수료까지 얼마남지 않았는뎅,, 남은 시간동안 강의를 열심히 들어야겠다.너무 빡빡한 일정이었지만 3주차까지 버텨오고 React에 대해 친해질 수 있는 시간이되어서 뿌듯하다.현업에서 Vue를 사용하다 기술 전환을 위해 React를 도입한다고 해서 급하게 스터디에 신청해서 힘들지만 조금만 더 버티면되지 않을까? 직장과 병행하니.. 이게 쉬운일은 아닌거 같다. 모든 직장인들 화이팅입니다!흑흑.. 미션 기능 중에 동작하지 않는게 있어서 수정하고 다시 배포하고 이제 자러간당ㅠㅠ다음주는 더 성장한 내가 되어 있기를!! 모두 완주까지 화이팅입니다~(2025.03.23 새벽 03:26)<끝>

프론트엔드워밍업스터디3기프론트엔드미션JSJavascriptReactRedux회고

치현

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

학습 내용인프런 워밍업 클럽 스터디 2주차로, 이번 주는 드롭 박스 프로젝트와 함께 Supabase의 Storage를 다뤄볼 수 있는 시간이었다. Supbase Storage1. 기본 구성 요소Files: 모든 종류의 미디어 파일 저장 가능 (이미지, GIF, 비디오 등)Folders: 파일을 체계적으로 구성하기 위한 디렉토리 구조Buckets: 파일과 폴더를 담는 최상위 컨테이너 (접근 규칙별로 구분)2. 접근 제어 모델Private Buckets (기본값) RLS(Row Level Security) 정책을 통한 접근 제어JWT 인증 필요Signed URL을 통한 임시 접근 가능Public Buckets파일 조회 시 접근 제어 없음URL만 있으면 누구나 접근 가능업로드/삭제 등 다른 작업은 여전히 접근 제어 적용3. 보안 기능RLS 정책 설정 가능SELECT (다운로드)INSERT (업로드)UPDATE (수정)DELETE (삭제)소유권 관리owner_id 필드로 리소스 소유자 추적JWT의 sub claim 기반 소유권 할당4. 이미지 변환 기능 (Pro Plan 이상)실시간 이미지 최적화크기 조정품질 조정 (20-100)WebP 자동 최적화변환 옵션resize 모드: cover, contain, fillwidth/height 지정 (1-2500px)최대 파일 크기: 25MB최대 해상도: 50MP5. 인증 방식S3 액세스 키서버 사이드 전용모든 버킷에 대한 완전한 접근 권한세션 토큰클라이언트 사이드 사용 가능RLS 정책 기반 제한된 접근6. 통합 기능Next.js 이미지 로더 지원AWS S3 호환성PostgreSQL DB와 연동7. 제한사항파일명은 AWS S3 명명 규칙 준수 필요HTML 파일은 보안상 plain text로 반환이미지 변환 기능은 Pro Plan 이상에서만 사용 가능미션 2 구현 내용과제 구현 저장소Dropbox 중파일의 마지막 수정(업로드) 시간을 표시하는 기능 추가 하기 export interface FileObject { name: string bucket_id: string owner: string id: string updated_at: string created_at: string last_accessed_at: string metadata: Record<string, any> buckets: Bucket }=> DropboxImage 컴포넌트가 prop으로 받는 image의 타입은 FileObject로 그 중 업로드시간은 created_at을 의미하기에 이를 이미지에 추가하였다.(사진 참고) 포인트 1: 한글 파일명 es-hangul 사용// 안전한 파일명 생성을 위한 유틸리티 export class FileNameConverter { // 안전한 문자 패턴 정의 private static readonly SAFE_CHARACTERS = /^[a-zA-Z0-9!\-_.*'()]+$/; private static generateRandomString(length: number = 8): string { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length)) ).join(""); } // 파일명이 안전한 문자들로만 구성되었는지 확인 private static isSafeFileName(name: string): boolean { return this.SAFE_CHARACTERS.test(name); } // 안전하지 않은 문자를 포함한 파일명을 안전한 형식으로 변환 private static convertToSafeFileName(name: string): string { try { // 파일명 정규화 const normalized = name.trim().normalize(); // 한글이나 특수문자가 있는지 확인 const hasKorean = /[ㄱ-ㅎㅏ-ㅣ가-힣]/.test(normalized); const hasSpecialChars = /[^A-Za-z0-9]/.test(normalized); if (!hasKorean && !hasSpecialChars) { return normalized; } // 한글이 있는 경우 로마자로 변환 시도 if (hasKorean) { const romanized = romanize(normalized); if (romanized && romanized !== normalized) { // 로마자 변환 결과에서 안전하지 않은 문자 제거 return romanized.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase(); } } // 변환 실패 시 랜덤 문자열 생성 return this.generateRandomString(); } catch (error) { console.error("Conversion error:", error); return this.generateRandomString(); } } // 원본 파일명을 안전한 형식으로 변환 static encode(fileName: string): string { console.log("Original filename:", fileName); const extension = fileName.split(".").pop() || ""; const nameWithoutExt = fileName.slice(0, fileName.lastIndexOf(".")); const safeName = this.isSafeFileName(nameWithoutExt) ? nameWithoutExt : this.convertToSafeFileName(nameWithoutExt); console.log("Safe filename:", safeName); return `${safeName}_${Date.now()}.${extension}`; } // 파일명에서 타임스탬프 제거하여 원본 이름 추출 static decode(fileName: string): string { const [name] = fileName.split("_"); return name || fileName; } }포인트 2 : 업로드 날짜 표시export function formatDate(timestamp: string): string { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); // 1일 이내 if (diff < 24 * 60 * 60 * 1000) { const hours = Math.floor(diff / (60 * 60 * 1000)); if (hours < 1) { const minutes = Math.floor(diff / (60 * 1000)); return `${minutes}분 전`; } return `${hours}시간 전`; } // 30일 이내 if (diff < 30 * 24 * 60 * 60 * 1000) { const days = Math.floor(diff / (24 * 60 * 60 * 1000)); return `${days}일 전`; } // 그 외 return date.toLocaleDateString("ko-KR", { year: "numeric", month: "long", day: "numeric", }); } 회고파일명 변환하는데 생각보다 시간이 많이 소요됐다.여찌저찌 구현은 헀지만, 이미지가 어떻게 encoding되고 decoding되는지 일련의 과정에 대한 공부가 필요함을 느끼는 이번주 였다.  

풀스택풀스택인프런워밍업스터디클럽Next3기SupabaseReact프론트엔드2주차발자국

강지원

[인프런 워밍업 스터디 클럽 2기 FE] 강지원 과제 제출

<JavaScript 과제> Day 2 Mission (음식 메뉴 앱)github : https://github.com/noaprost/inflearn_study_js_mission/tree/main/mission1개발 시간 : 3시간개발 순서메뉴 정보를 담을 menu.json 파일을 만들었습니다.fetch를 사용하여 json 데이터를 불러오고 main 부분에 동적으로 child를 추가해주었습니다.각 카테고리 button에 onClick eventListener를 달아주고 curCategory 변수를 선언하여 현재 클릭 된 카테고리가 무엇 인지에 따라 다른 child가 main에 보여지도록 했습니다.아쉬웠던 점useEffect나 useState를 대체할 코드를 js로 짜는 것이 미숙해서 코드가 깔끔하게 짜이지 않은 것 같아 아쉽습니다.카테고리 버튼을 누를 때마다 main에 이미 있는 child를 모두 제거 후 다시 필터링해서 append 해주었는데, 코드가 비효율적인 것 같아서 아쉽습니다.느낀 점첫 과제를 무사히 끝내서 다행이다. 이번 과제는 바닐라 js로만 앱을 만든 것이 3년전 이후로 처음이라 많이 해맸지만 앞으로의 과제를 만들면서 js 실력이 많이 길러질 것 같아서 기대가 됐다. main 화면Breakfast를 클릭한 화면  Day 3 Mission (가위 바위 보 앱)github : https://github.com/noaprost/inflearn_study_js_mission/tree/main/mission2개발 시간 : 2시간구현 사항가위, 바위, 보 버튼을 누르면컴퓨터에서 랜덤 변수를 생성하고 플레이어와 승패를 비교합니다.승패에 따라 승리 횟수를 업데이트 해주고, 남은 횟수를 1 감소 시킵니다.남은 횟수가 모두 소진됐을 경우최종 승리 횟수를 비교해서 알맞는 문구를 띄워주고 replay 버튼을 보여줍니다.replay 버튼을 누를 경우모든 횟수가 초기화됩니다. 느낀 점한번 만들어봤던 앱이라서 바로 만들기부터 시작했는데, 중간에 막혀서 고생했다ㅠ 구현할 기능과 순서를 다시 쭉 적어 놓고 구현하니까 순조롭게 만들어져서 요구 사항 작성의 중요성을 다시 한번 깨달았다. main 화면 (게임 중)게임이 끝난 화면  Day 4 Mission (퀴즈 앱)github : https://github.com/noaprost/inflearn_study_js_mission/tree/main/misson3개발 시간 : 2시간구현 사항랜덤으로 1~50사이의 수를 골라서 문제를 생성합니다.랜덤으로 정답+랜덤 수 / 랜덤 수1+랜덤 수2+정답이 없습니다. 둘 중 하나를 보여줍니다.정답을 누를 경우 Next 버튼이 보여지고 버튼을 클릭하면 다음 문제로 이동합니다.문제를 틀렸을 경우 Restart 버튼이 보여지고 버튼을 클릭하면 새로운 문제를 보여줍니다. 아쉬웠던 점정답/틀린 답을 클릭했을 경우 ui를 보여줄 때 class 이름을 추가하고 삭제하는 방식으로 구현했는데, if문도 많이 중첩되어있고 긴 코드를 작성한 것 같아서 아쉽습니다. 추후에 더 나은 코드로 리팩토링을 하고 싶습니다. 만족한 점답 버튼을 최대한 랜덤으로 보이게 하고 싶어서 수를 먼저 만들고 이후에 동적으로 생성한 점이 만족스럽습니다. 느낀 점생각보다 구현할 때 신경 쓸 부분이 많았고, ui 처리가 어려웠지만 재밌게 만들었다. 영상에 요구 사항이 잘 안보여서 규칙 등을 직접 정하면서 만들었는데 그 점도 재밌었다. 문제를 띄운 화면정답을 클릭했을 경우오답을 클릭했을 경우  Day 5 Mission (책 리스트 나열 앱, Github Finder 앱) 책 리스트 나열 앱github 주소 : https://github.com/noaprost/inflearn_study_js_mission/tree/main/mission4개발 시간 : 1시간구현 순서form을 만들어서 input 값 2개를 받는다.submit 버튼을 누르면 input에 있던 값을 다른 변수에 저장해둔다.데이터를 가공해서 리스트에 책 제목과 저자, 삭제 버튼을 담은 목록을 보여준다.삭제 버튼을 누르면 해당 리스트가 삭제된다.느낀 점이번 과제는 개인적으로 정말 쉬웠다. todo 앱 강의를 먼저 듣고 나서 만들어서 그런지 가장 명확하게 구현 순서가 그려져서 재미있었다. 책 생성 화면책 삭제 화면Github Finder 앱github 주소 : https://github.com/noaprost/inflearn_study_js_mission/tree/main/mission5개발 시간 : 7시간구현 순서Github api로 user정보와 repo정보를 가져온 후, repo 정보는 최근에 작성된 것이 먼저 보여지도록 정렬한다.displayUserInfo와 displayLatestRepo 함수를 이용해서 각각의 정보를 화면에 보여준다.필요한 정보 정리// user data에서 가져올 목록 // 맨위 4개 태그 public_repos public_gists followers following // 리스트 info company blog location created_at // View Profile 버튼 html_url // 최근 리포 baseUrl repos_url // repo data에서 가져올 목록 // Latest Repos name -> html_url 이용해서 이동할 수 있도록 stargazers_count watchers_count forks_count 어려웠던 점새로운 user를 검색할 때 마다 userInfo와 latestRepo 컨테이너에 담긴 정보들을 지워주고 새로 담아야 하는데, 아래의 간단한 코드를 떠올리지 못해서 3시간이나 해맸다.document.getElementById("user-info").innerHTML = ""; document.getElementById("latest-repo").innerHTML = ""; 느낀 점처음 user의 정보를 가져오는 BaseUrl을 찾는 것이 어려웠지만 url을 찾고 나니 나머지는 데이터 가공 뿐이라서 만드는 과정은 재밌었다. main 화면검색 결과 화면 (user info)검색 결과 화면 (latest repo)  <React 과제> Day 9 Mission (예산 계산기)github 주소 : https://github.com/noaprost/inflearn_react_mission_budget_calculator개발 시간 : 4시간구현 사항예산 목록을 생성, 수정, 삭제할 수 있어야 한다.목록 지우기 버튼을 누르면 목록 전체가 삭제되어야 한다.생성, 삭제, 수정 시 맨 위에 status를 알려주는 상태 메세지를 띄워준다.총 예산을 계산해서 가장 아래쪽에 표시해준다. 느낀 점그동안 Next.js, Three.js 공부를 하느라 React 프로젝트를 오랫만에 만들었더니 간단한 CRUD 앱인데도 꽤나 어렵게 구현하였다. 역시 꾸준한 것 보다 좋은 것은 없는 것 같다ㅠㅠ 과제를 진행하면서 잠시 잊었던 감을 다시 찾아야겠다는 생각이 들었다. main 화면예산 입력 후 화면예산 수정 화면  Day 10 Mission(디즈니 플러스 앱)github 주소 : https://github.com/noaprost/disneyplus_clone개발 기간 : 약 2일페이지 별 구현 사항로그인 페이지firebase를 이용해서 구글로 로그인을 한다.로그인 버튼을 누르면 popup창이 뜨고 로그인이 되면 홈으로 이동한다.Navbaruser가 있으면 프로필 사진이 원형으로 보여지고, 없으면 LOGIN 버튼이 보여진다.disney 로고를 누르면 user가 있을 경우 "/home"으로 이동, 없을 경우 "/"로 이동한다.user 프로필 사진을 hover하면 LOGOUT 버튼이 1.5초간 보여지고 클릭 시 user 정보가 사라진다.메인 페이지axios를 이용해서 영화 목록을 받아온 뒤 영화 하나의 정보를 보여준다. (이미지, 제목, 설명)영화사 버튼을 만들고, hover 시 영상이 재생되도록 한다.인기 영화, 평점 높은 영화, 판타지 영화, 액션 영화 각 카테고리에 맞는 영화들의 이미지를 보여준다.카테고리에 보여지는 영화들은 swiper를 이용해서 감싸준다.카테고리에 보여지는 영화를 클릭할 경우 상세 정보를 담은 모달이 나오고, 화면 밖을 클릭할 경우 모달이 닫히게 해준다.Modal이미지, 개봉일, 제목, 평점, 설명을 보여준다.메인 페이지에 오면 검색 창이 나타나고, 검색어 입력 시 검색 페이지로 이동한다.검색 페이지에서 메인 페이지로 돌아오게 되면 useLocation을 이용해서 검색어를 초기화해준다.검색 페이지로 이동할 때 검색어 정보를 query에 담아 url에 파라미터로 보내준다.검색 페이지useLocation으로 받은 데이터를 가공해서 검색어를 저장한다.검색어가 변경될 때마다 검색어를 query로 받는 검색 결과를 업데이트 해준다.결과의 이미지를 목록의 형태로 보여준다.이미지를 클릭할 경우 화면에 꽉 찬 이미지가 보여진다. 느낀 점이번 프로젝트를 하면서 React, CSS, Html, JS에 대해 새로운 것을 많이 알게 되었다. 과제를 하는 동안에는 에러도 많이 나고, 데이터가 안받아지거나, 페이지가 예상한대로 동작하지 않아서 스트레스도 많이 받아가며 만들었지만, 그 과정에서 실력은 확실히 성장할 수 있었다. 끝나고 나니까 뿌듯하고 계속 보게 됐다. 아쉬운 점카테고리 별로 영화 정보를 받아올 때, 인기 영화와 평점 높은 영화를 받아오는 api 주소와 장르별 영화를 받아오는 api 주소가 달라서 서로 다른 컴포넌트에서 로직을 짰다. 이것 때문에 swiper 코드와 Modal 코드, 목록을 보여주는 코드들이 중복이 된 것 같아서 아쉽다. 로그인 페이지메인 페이지메인 페이지 (영화사 버튼 hover 시)메인 페이지 (카테고리 별 목록)메인 페이지 (영화 상세 정보 모달)검색 페이지검색 페이지 (이미지 클릭 시)  Day 12 Mission (퀴즈 앱)github 주소 : https://github.com/noaprost/quiz_app개발 시간 : 3시간라우터 구조 및 구현 사항"/"Welcome to Quiz App을 보여준다."/question"수학 문제 2개를 보여준다.라디오 버튼을 클릭하면 답변 확인 버튼이 뜬다.답변 확인 버튼을 클릭하면 정답을 클릭했을 경우와 오답을 클릭했을 경우를 나눠서 보여준다.정답을 클릭했을 경우 border color를 green으로, 오답을 클릭했을 경우 red로 보여준다.정답인 label에 v표시를 붙여주고, 오답인 label에는 x를 붙여준다."/state"math와 alphabet 중에 하나를 선택할 수 있는 select box를 보여준다.선택하면 카테고리에 맞는 문제를 보여준다.채점 방식은 question과 동일하다."/quiz"퀴즈를 보기 전math와 alphabet 중에 하나를 선택할 수 있는 select box를 보여준다.연습 테스트 시작 버튼을 보여준다. 클릭하면 퀴즈 화면으로 변경된다.퀴즈를 보는 중문제와 남은 문제 수가 보여진다.답을 선택하면 다음 버튼이 보여지고, 마지막 문제였을 경우 제출 버튼이 보여진다.다음 버튼을 선택하면 다음 문제가 보여지고, 제출 버튼을 누르면 결과 화면으로 변경된다.퀴즈를 본 후총 문제 수 중 몇 문제를 맞췄는지 보여준다. (합격했을 경우 초록색, 아닌 경우 빨간색)시험 합격/불합격 여부가 보여진다. (4개 전부 맞을 경우 합격, 아닌 경우 불합격)새로운 연습 테스트 시작 버튼이 보여진다.버튼 클릭 시, 퀴즈를 보기 전 화면으로 돌아간다. 아쉬운 점문제를 랜덤으로 동적 생성하고 퀴즈의 문제 수도 설정할 수 있도록 구현해보고 싶었는데 시간이 부족해서 구현하지 못했다. 스터디가 끝난 후에라도 혼자서 개발해보고 싶다. 느낀 점그래도 가장 최근에 개발할 때 사용했던 Next.js와 Typescript여서 비교적 편하게 개발했던 것 같다. 구조들이나 변수가 반복되는 부분이 많다 보니 코드를 가독성 있게 짜기가 쉽지 않았다. 리팩토링에 대한 공부의 필요성이 느껴졌다. home 화면question 화면 (문제를 풀기 전)state 화면 (문제를 푼 후)quiz 화면quiz 화면 (연습 테스트 시작)quiz 화면 (테스트 결과) 

JSReact인프런스터디과제미션

강지원

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

따라하며 배우는 리액트 A-Z (7~13강) Day 11 (리액트 테스트 경험하기) TDD (Test Driven Development)개발 전 테스트 코드를 먼저 작성하는 방법론 순서테스트 코드를 작성 → 실행 (당연히 실패) → 딱 이 테스트 코드를 통과할 만큼의 코드만 작성 → 성공 → 다른 테스트 코드 작성으로 이동전부를 다 한 다음에 리팩토링 작업을 실행여기서 문제가 된다면 다시 테스트 코드를 확인하고 수정함 TDD의 장점소스 코드에 안정감을 줌디버깅 시간과 개발 시간이 줄어듦시스템 전반적인 설계 향상 (클린 코드가 나올 확률이 높아짐)개발 집중력 향상 (테스트를 하나씩 깨나가면서 개발할 수 있기 때문)  Day 12 (NextJS, 타입스크립트)내용을 복습하면서 원래 알고 있던 것 + 새로 알게 된 내용을 공부하는 방식으로 공부했다.특히 TS의 타입 들을 코드로 정리하면서 어떤 타입이 있었는지 다시 새길 수 있었다. Next.jsReact의 SSR을 쉽게 구현할 수 있게 도와주는 간단한 프레임 워크 특징 Full-StackFile-based RoutingSEO, Image, Font OptimizationServer Side RenderingHybrid Rendering CSR (Client Side Rendering)리액트만으로 프로젝트를 만들면 발생 → 렌더링 하는 주체가 클라이언트 (브라우저) 장점한번 로딩 되면, 빠른 UX 제공서버의 부하가 작음 단점페이지 로딩 시간(TTV)이 김 (FCP가 오래 걸린다고도 함)자바스크립트 활성화가 필수임SEO가 힘듦보안에 취약 (모든 react 라이브러리, js 소스 코드를 다 다운 받아야 하기 때문에)CDN(Content Delivery Network)에 캐시가 안됨 → 이러한 많은 단점들을 해결하기 위해서 나온 것이 SSR SSR (Server Side Rendering렌더링 하는 주체가 서버렌더링 되는 순간 : 요청 시 렌더링 (미리 해두지 않음) 장점페이지 로딩 시간(TTV)이 빠름자바스크립트 활성화 여부가 필요 없음SEO 최적화가 좋음보안이 뛰어남CDN에 캐시가 됨실시간 데이터를 사용!사용자 별 필요한 데이터를 사용함 (요청 시 렌더링 하기 때문에 요청하는 사람에 맞게 데이터 제공이 가능) 단점비교적 느릴 수 있음서버에 과부하가 걸릴 수 있음 (몇 천명의 페이지를 요청 시 마다 계속 만들어야 하기 때문에)CDN에 캐시가 안됨 (요청마다 새로운 페이지를 만들기 때문에) Typescript 타입 : value가 가지고 있는 프로퍼티나 함수를 추론할 수 있는 방법JS의 dynamically type이 야기할 수 있는 runtime error를 해결하기 위해 statically type을 적용 시킨 언어 사용하는 이유Typescript는 JS코드를 단순화하여 더 쉽게 읽고 디버그할 수 있도록 함Typescript는 코드 유형 검사를 통해 JS를 작성할 때 고통스러운 버그를 피할 수 있도록 도와줌  Day 13 (리덕스 학습하기 및 리액트 19) ReduxJS 어플리케이션을 위한 상태 관리 라이브러리 data flowAction간단한 JavaScript 객체수행하는 작업의 유형을 지정하는 'type' 속성이 있으며 선택적으로 redux 저장소에 일부 데이터를 보내는 데 사용되는 'payload' 속성을 가질 수도 있음 Reducer어플리케이션 상태의 변경 사항을 결정하고 업데이트 된 상태를 반환하는 함수store 내부의 상태를 업데이트함 Redux Store어플리케이션의 전체 상태 가지고 있는 객체내부 상태를 변경하는 유일한 방법은 해당 상태에 대한 Action을 전달하는 것 Redux 미들웨어action을 dispatch하고 reducer에 도달하는 순간 사이에서 사전에 지정된 작업을 실행할 수 있게 해주는 중간자 Redux Thunk비동기 작업을 할 때 많이 사용하는 방법Redux Thunk도 리덕스 미들웨어액션 내부에서 다양한 작업을 할 수 있게 해줌 Redux ToolkitRedux 로직을 작성하기 위한 공식 권장 접근 방식Redux 코어를 둘러싸고 있고, Redux 앱을 빌드하는 데 필수적인 패키지와 기능이 포함되어 있음Redux 작업을 단순화하고 일반적인 실수를 방지하며, Redux 앱을 더 쉽게 작성할 수 있도록 도와줌 느낀 점 redux의 기본 사용 법을 처음으로 배우는 것이라 신기하고 어렵게 느껴졌다. 강의를 따라가면서 기초에 집중하려고 노력했는데, 처음 배운 개념을 이용해서 앱을 만드려니 꽤나 막막해서 시간이 많이 걸렸다. 앞으로 더 능숙해지고 싶다. 일주일 회고스터디 중간에 몸이 3일 동안 아팠어서 시간이 많이 없었지만 열심히 달려서 겨우 완주 조건까지 달성했다.매일 새벽까지 과제를 개발한 덕에 JS 과제 5개와 React 과제 3개를 해냈다. 모든 과제를 해내면 신입 역량 이상이라고 하셨었는데 나는 아직 신입 역량 정도였나 보다. 아쉽다.그동안 머릿속에 스터디만 가득한 채로 3주를 보냈는데 막상 끝나려고 하니까 아쉽지만 3주전의 나와 지금의 나를 비교했을 때 많은 성장을 이뤄낸 것 같아서 뿌듯하다.이번에 알게 된 부족한 점들을 바탕으로 앞으로의 공부 계획을 세워나갈 예정이다.정말 의미 있는 시간이었고, 꼭 필요했던 시간이라고 생각한다. 강의 출처강의 주소(React) : https://www.inflearn.com/course/따라하는-리액트/코치님 성함 : John Ahn

ReactNextJSTypescriptRedux프론트엔드인프런스터디3주차

강지원

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

따라하며 배우는 자바스크립트 A~Z (8~10강)따라하며 배우는 리액트 A-Z (1~6강) Day 6 (Iterator, Generator, Design Pattern)디자인 패턴* 프로그래머가 응용 프로그램이나 시스템을 디자인할 때 일반적인 문제를 해결하는데 사용할 수 있는 공식화된 모범 사례 * 최고의 솔루션 * 재사용성 * 풍부한 표현력 * 향상된 의사소통 * 코드 리팩토링 필요x * 코드 베이스 크기 감소Singleton* 클래스의 인스턴스화를 하나의 객체로 제한하는 디자인 패턴 * 시스템 전체에서 작업을 조정할 때 정확히 하나의 객체만 필요한 경우 유용Factory* 특수 함수인 팩토리 함수를 사용하여 비슷한 객체를 많이 만들 수 있는 패턴 * 비슷한 객체를 반복적으로 생성해야 하는 경우 사용 * 동일한 코드를 반복할 필요 없게 해줌Mediator* 객체 그룹에 대한 중앙 권한을 제공하는 패턴 * 대표 예시 : 채팅방 * 중재자한테 먼저 채팅을 보내고 원래 보내려던 사람에게 중재자가 전달 * 정크 메세지 등을 거를 수 있음 Observer* event-driven 시스템을 이용하는 패턴 * 특정 subject를 관찰하는 많은 observer가 있고, 해당 subject에 변경 사항이 있을 떄, 알림 받기를 원하기 때문에 스스로를 등록(Register)함 * 관심을 잃게 되면 등록을 취소 * 예시 : 유명 연예인을 팔로워한 대중들 Module* 코드를 더 작고 재사용 가능한 조각으로 분할하는 패턴 * <script type="module"></script> 로 표현 가능 * 특징 * 1. 항상 strict 모드로 실행됨 * 2. 지연 실행됨 (defer) * 3. 인라인 모듈 스크립트도 비동기 처리할 수 있음 * 4. 외부 origin에서 스크립트를 불러오려면 Cors 헤더가 있어야 함 * 5. 증복된 스크립트는 무시됨 * 모듈 레벨의 스코프 * 모듈 안에 자신만의 스코프가 만들어져서 모듈 내부에 정의된 변수나 함수는 * 다른 스크립트에서 접근할 수 없게 됨 * 모듈은 최초 호출시 한번만 실행됨 * 스크립트 안에서 정보를 가져오기 위해서는 * import.meta를 사용해야 함 * async * 일반 스크립트에서 async -> 외부 스크립트에만 해당 * 모듈 스크립트에서 async -> 외부, 인라인 스크립트 둘 다 해당 Day 7 (프로젝트 만들기)StopWatch App// 지정된 timeout ms가 지날 때마다 handler를 실행해주는 함수 setInterval(handler, timeout) // setInterval에서 실행한 내용을 reset시키는 함수 clearInterval()Todo App// child 요소를 맨 앞으로 넣어주는 함수 prepend() Spreadsheet App // csv 파일을 다운로드할 수 있는 버튼을 만드는 방법 const csvObj = new Blob([csv]); const csvUrl = URL.createObjectURL(csvObj); const a = document.createElement("a"); a.href = csvUrl; a.download = "Spreadsheet File Name.csv"; a.click();백한테 파일 형태로 요청을 보낼 때만 써봤던 Blob을 이용해서 파일 다운로드 버튼을 만들어보다니 신기했다. Day 8 (중간 점검)라이브러리를 잘 활용하는 것도 실력이라고 하신 점이 기억에 남았습니다.너무 어려운 기능이 있으면 막막해서 라이브러리를 써볼까 하다가도 과연 개발자로써 직접 만들지 않고 이미 있는 것을 가져다 사용하는 것이 옳은 걸까라는 생각을 많이 했었는데, 이 부분을 확실하게 꼬집어주시면서 해결책을 주셔서 좋았습니다.코치 님의 말씀을 들으면서 라이브러리 사용을 두려워하지 말고 적극적으로 활용하는 개발자가 되어야겠다고 생각했습니다.그리고 third party api라는 단어를 잘 모르겠어서 찾아봤는데, 기업들이 이윤 창출을 목적으로 개발한 API라는 것을 알게 되었습니다. 예를 들어 구글을 통해 로그인, 구글맵 등이 third party api에 해당합니다. 예시로 든 두 기능은 프로젝트를 개발할 때 사용해봤던 api였는데도 불구하고 정확한 용어를 몰랐다는 점이 부끄러웠습니다. 앞으로 잘 기억해둬야겠습니다. Day 9 (리액트 기본 및 Todo 앱 만들기)React는 UI를 만들기 위한 JS 라이브러리 즉, Renders UI and responds/reacts to eventsSPASPA : Single Page Application 사용자가 버튼을 클릭했을 때 다른 페이지를 보여주는 것이 아니라 한 페이지 내에서 어떤 건 숨기고 어떤 건 보여주면서 동작하는 방식 성능 개선 부분이 너무 어려워서 코드를 따라치는 수준으로 겨우 들었다.. 추가로 공부를 더 하면서 완전히 내 것으로 만들어야겠다. Day 10 (리액트로 Netflix 만들기)강의를 보면서 더 공부하고 싶었던 내용들* Styled Component * Modal * ReactRouter * useLocation * useDebounce * useParamsTMDB api는 처음 사용해봤는데 넷플릭스, 디즈니 플러스 등 영화 정보가 필요한 앱을 만들때 굉장히 유용할 것 같다.여러 훅을 사용해 볼 수 있어서 좋았지만 한번에 이해하기는 힘든 부분이라 더 공부가 필요할 것 같다. Styled Component 내용 정리 및 개인적인 장단점 정리* CSS-in-JS JS 코드 내에서 잘 스타일링 된 컴포넌트를 직접 만들어서 가져다가 쓰는 방식 * 장점 - css 파일을 왔다 갔다 하지 않고 사용할 수 있다. * 단점 - JS파일 내에서 동작하기 때문에 수정하고 빌드할 때마다 재 컴파일 되어서 성능이 안좋음 - 스타일링과 컴포넌트를 오가며 작성해야 하는 불편함이 있음 - 컴포넌트만 봤을 때 이게 stlyed된 컴포넌트 인지 그냥 컴포넌트인지 구분할 방법이 없음 일주일 회고저번 주는 약과다 싶을 정도로 많이 바쁜 일주일이었다. 그래도 이렇게 무엇인가에 열중하는 경험은 몸이 피곤하더라도 기분은 좋다. 다음 주가 벌써 마지막 주라니.. 시간이 정말 빠르다. 마지막 주까지 힘내서 강의도 완주하고 과제도 잘 만들어야겠다.저번 주 회고 때 너무 많은 내용을 담은 것 같아서 아쉽다고 했었는데 확실히 저번 주 보다는 꼭 기억하고 싶은 내용들만 적어서 그런지 간략하고 가독성 있게 쓴 것 같아서 만족한다. 강의 출처강의 주소(JS) : https://www.inflearn.com/course/따라하며-배우는-자바스크립트/강의 주소(React) : https://www.inflearn.com/course/따라하는-리액트/코치님 성함 : John Ahn

JSReact인프런스터디프론트엔드2주차

치현

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

학습 내용인프런 워밍업 클럽 스터디 3주차로,이번 주는 넷플릭스 프로젝트를 다루는 시간이었다.useInfiniteQuery와 Jotai(recoil 대체 전역상태 라이브러리)를 사용해볼 수 있었다.미션 3 구현 내용과제 구현 저장소Netflix 중 찜하기 관련 기능 포인트 1: favorites 테이블 추가actions/favoriteActions.ts"use server"; import { createServerSupabaseClient, handleError, PostgrestError, } from "@next-inflearn/supabase"; // Movie 타입 정의 export type Movie = { id: number; image_url: string; overview: string; popularity: number; release_date: string; title: string; vote_average: number; // Movie 타입에 favorites 필드 추가 favorites?: { // optional field로 추가 id: number; } | null; }; // SearchMoviesResponse 타입 정의 export type SearchMoviesResponse = { data: Movie[]; page: number; pageSize: number; hasNextPage: boolean; }; // 에러 케이스를 위한 타입 정의 type SearchMoviesError = { data: never[]; count: number; page: null; pageSize: null; error: PostgrestError; }; // 성공 케이스를 위한 타입 정의 type SearchMoviesSuccess = { data: Movie[]; page: number; pageSize: number; hasNextPage: boolean; }; export async function searchMovies({ search, page, pageSize, }: { search: string; page: number; pageSize: number; }): Promise<SearchMoviesSuccess> { const supabase = await createServerSupabaseClient(); // 현재 사용자 정보 가져오기 const { data: { user }, } = await supabase.auth.getUser(); // 쿼리 설정 const query = supabase .from("movie") .select( user ? ` *, favorites!left ( id ) ` : "*", // 로그인하지 않은 경우 favorites 정보를 가져오지 않음 { count: "exact" } ) .ilike("title", `%${search}%`) .order("popularity", { ascending: false }); // 로그인한 경우 현재 사용자의 즐겨찾기만 조회하도록 필터링 if (user) { query.eq("favorites.user_id", user.id); } const { data, count, error } = await query.range( (page - 1) * pageSize, page * pageSize - 1 ); const hasNextPage = count ? count > page * pageSize : false; if (error) { return { data: [], page, pageSize, hasNextPage: false, }; } // 반환된 데이터를 Movie 타입에 맞게 변환 const moviesWithFavorites = (data || []).map((movie: any) => ({ ...movie, favorites: movie.favorites?.[0] || null, // 즐겨찾기 정보 포함 })); return { data: moviesWithFavorites, page, pageSize, hasNextPage, }; } export async function getMovie(id: string) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase .from("movie") .select("*") .eq("id", id) .maybeSingle(); handleError(error); return data; } 포인트 2: profiles 테이블 추가utils/AuthProvider.tsx"use client"; import { useEffect } from "react"; import { createBrowserSupabaseClient } from "@next-inflearn/supabase"; import { useSetAtom } from "jotai"; import { userAtom } from "@/utils/jotai/atoms"; export function AuthProvider({ children }: { children: React.ReactNode }) { const setUser = useSetAtom(userAtom); const supabase = createBrowserSupabaseClient(); useEffect(() => { // 현재 세션 확인 supabase.auth.getSession().then(({ data: { session } }) => { setUser(session?.user ?? null); }); // Auth 상태 변경 구독 const { data: { subscription }, } = supabase.auth.onAuthStateChange((_event, session) => { setUser(session?.user ?? null); }); return () => subscription.unsubscribe(); }, [supabase, setUser]); return children; } 포인트 3: movies / favorites / profiles 테이블 연결favorites 테이블 내 profiles 테이블 내 회고 시간이 부족하다는 핑계로,더 디벨롭할 수 있는 부분이 많음에도 불구하고 생각했던 것들을 다 구현하진 못했던 것 같다.다만, 이렇게 틀을 갖춰놧으니 추후에 추가적인 기능을 정의해서 구현해보기 너무 좋을 것 같다.  

풀스택풀스택인프런워밍업스터디클럽Next3기SupabaseReact프론트엔드3주차발자국

바다다다

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

수강강의1: 따라하며 배우는 자바스크립트 A-Z수강강의2: 따라하며 배우는 리액트 A-Z[19버전 반영]Tip: 워밍업 스터디 클럽을 참여하게 되면 [할인쿠폰]으로 강의를 할인 된 가격에 수강할 수 있다!2주차 강의 수강2주차를 되돌아 보며..이번주에는 지난주 Javascript강의를 마무리하고 드디어 React에 대한 학습에 진입하게 되었다.Javascript후반부 강의에서는 주로 디자인 패턴, Iterator, Generator에 대해 알아가고 마지막으로 Todo app, spread sheet 등을 따라 만들어 보면서 Javascript문법에 대해 조금 더 익숙해지고 복습하는 시간을 가지었다.React는 초반에 설치와 프로젝트 환경설정을 시작으로 처음부터 앱을 따라 만들어 보면서 React의 구조에 대해 알아보았다. TODO앱과 Netflix따라 만들기 강의를 따라서 코딩을 진행을 하였지만 환경설정이나 알수없는 오류등이 발생해서 바로 미션 과제를 중점으로 학습을 진행하였다.강의에서 언급한 이론을 실제 미션 과제에서 실습으로 결과물을 얻을 수 있어서 뿌듯했다. 지난주 과제에 비해 난이도가 많이 높아져서 진도를 따라잡기에 많이 버거웠던것도 사실이다. (특히 직장인이라면...More and more...hard..)강의 내용을 다시 정리해보며..(주로 개인적으로 새롭게 알게된 것, 다시 기억해야하는 것들 위주로 정리했다)Generator Generator사용자의 요구에 따라 다른 시간 간격으로 여러 값을 반환할 수 있음일반 함수 → 한번 실행으로 끝까지 실행됨generator 함수 → 일시적으로 정지될 수도 있고, 다시 시작할 수도 있음function* sayNumbers() {...} 별표로 generator함수 생성yield 함수를 정지시킴 → 일반함수에서 return과 동일function* sayNumbers(){ yield 1; yield 2; yield 3; } const number = sayNumbers() console.log(number.next()) // {value:1, done:false} console.log(number.next()) // {value:2, done:false} console.log(number.next()) // {value:3, done:false} console.log(number.next()) // {value:undefined, done:true} lazy evaluation에 활용가능계산의 결과값이 필요할 때까지 계산을 늦춰서 필요한 데이터를 필요한 순간에 생성Singleton Pattern클래스의 인스턴스화를 하나의 객체로 제한하는 디자인 패턴. 시스템 전체에서 작업을 조정하는 데 정확히 하나의 객체가 필요한 경우에 유용하다클래스가 존재하지 않는 경우 클래스의 새 인스턴스를 생성하는 메서드로 클래스를 생성하여 구현할 수 있음. 인스턴스가 이미 존재하는 경우 해당 개체에 대한 참조를 반환⇒ 객체는 1개만 만들고 그것을 계속 재사용하는 것 (값이 한곳에서 바뀌면 전역으로 바뀜)Factory Pattern특수 함수인 팩토리 함수를 사용하여 비슷한 객체를 많이 만들 수 있음⇒ 비슷한 객체를 반복적으로 생성해야하는 경우 사용Mediator Pattern (중재자 패턴)객체 그룹에 대한 중앙 권한을 제공한다. (예: 채팅방)Observer Patternevent-driven시스템을 이용하는 것 (예: facebook알림)topic에 대해 register하고 publisher-subscriber구조를 가지게 됨Module Pattern코드를 더 작고 재사용 가능한 조각으로 분할하는 것을 도와준다js코드를 작성하고 export지시자를 변수나 함수 앞에 붙이면 외부 모듈에서 해당 변수나 함수에 접근가능하게 된다import로 외부 모듈을 가져올 수 있음모듈은 특수한 키워드나 기능과 함께 사용되므로 HTML에서 script태그를 넣을 때 type='module' 속성을 추가해줘야 한다항상 strict모드로 실행된다지연 실행 된다인라인 모듈 스크립트도 비동기 처리할 수 있다외부 orgin에서 스크립트를 불러오려면 CORS헤더가 있어야 한다중복된 스크립트는 무시한다 (최초 호출될 때 한번만 실행)구형브라우저에서는 <script nomodule> ... </scirpt> 로 module을 지원하지 않을 경우 예외처리를 할 수 있음리액트는 프레임워크가 아니라 라이브러리React는 라이브러리, Vue, Angular는 프레임워크왜 라이브러리? 리액트는 전적으로 UI를 렌더링하는 것에 관여하기 때문상태관리, 라우팅, 테스트 등등을 위해 다른 라이브러리가 추가적으로 필요함 (vue, angular는 이런 것들이 이미 포함되어 있음)프레임워크: 어떠한 앱을 만들기위해 필요한 대부분의 것을 가지고 있는 것라이브러리: 어떠한 특정 기능을 모듈화 해놓은 것프레임워크는 라이브러리의 집합리액트 컴포넌트 - Component리액트 앱을 이루는 최소한의 단위, 여러 컴포넌트를 조합하여 하나의 페이지가 완성되는 것클래스형 컴포너트 - class component함수형 컴포넌트 - funcional component브라우저가 그려지는 원리와 가상돔리액트의 주요 특징 중 하나가 가상돔을 사용하는 것이다웹 페이지 빌드 과정 (CRP) - Critical Render Pathbrowser가 서버에서 페이지에 대한 HTML문서를 응답으로 받고, 해당 문서를 읽는다. 그 후 스타일을 입히고 뷰포트에 표시하게 된다DOM tree생성 → Render Tree생성(화면에 표시되는 모든 노드의 콘텐츠 및 스타일 정보를 포함) → Layout(reflow) → Paint(화면에 그리기)화면에서 DOM에 변화가 생기면 Render Tree부터 다시 랜더링 해야한다는 문제점!! → 비효율적→ 가상돔: 실제 DOM을 메모리에 복사해준 것데이터가 바뀌면 가상돔에 랜더링되고 이전에 생긴 가상돔과 비교해서 바뀐 부분만 실제 돔에 적용 시킴. 바뀐 부분을 찾는 과정을 diffing이라고 부르고, 바뀐 부분만 실제 돔에 적용시켜주는 것을 재조정(reconciliation)이라 한다.  강의 내용을 들으면서 아쉬웠던 점.. 강의 내용에 대해서 불만족 스러운 부분은 없다! 너무 알차게 구현되어진 강의이고 다만 진도에 따라서 많은 양의 강의를 한꺼번에 들어야 하다보니 모두 소화하기에는 어려웠다.강의 외에 미션과제도 수행해야 하다 보니 시간적으로 지난주에 비해 많이 부족했다. 아래 미션과제에서도 볼 수 있겠지만, 지난주와 달리 기본적인 모든 기능을 구현하지는 못했다.(특히! 디즈니플러스 클론코딩ㅜ)2주차 미션 완료Javascript미션은 지난주에 이어서 그나마 완성하기 했으나,React미션은 초보자의 입장에서 강의내용 수준으로 따라가기에는 어려웠다.처음이다보니 모든게 낯설게 느껴졌고, 그래도 다른 강의 및 자료를 통해 많은 학습을 할 수 있었다.특히 컴포넌트를 나누는 기준과 React에서 컴포넌트를 조합해서 웹 페이지를 구성한다는 것을 미션 실습과제를 통해 좀 더 쉽게 깨달을 수 있었던 것 같다. Javascript - Mission6: 비밀번호 생성기Demo: https://rim0703.github.io/React-study/Javascript/6-pw-generator/Source Code: https://github.com/rim0703/React-study/tree/main/Javascript/6-pw-generator Javascript - Mission7: 타이핑 테스트Demo: https://rim0703.github.io/React-study/Javascript/7-typing-test/Source Code: https://github.com/rim0703/React-study/tree/main/Javascript/7-typing-test React - Mission1: 예산 계산기Demo: https://gentle-starburst-b5ca45.netlify.app/Source Code: https://github.com/rim0703/React-study/tree/react/mission1 React - Mission2: 디즈니 플러스 앱Demo: https://disney-plus-clone-mission2.netlify.app/Source Code: https://github.com/rim0703/React-study/tree/react/mission2참고자료: https://www.youtube.com/watch?v=3NHYl0Lo74A이건 할말이 많은데.. 사실 아래 데모처럼 메인화면만 완성했다. 맨땅에서 시작하기에는 너무 막막해보여서 유튜브에 클론코딩 영상이 있어서 따라 만들어보았다.추가로 메인 로그인 화면, 영화 상세 내용 팝업화면이 추가로 구현일 필요하다.(추후 시간이 있으면 보완할 예정!)마지막으로 2주차 회고이번 한주간은 너무 정신없이 지난거 같다. 강의, 미션과제 외에 처음으로 접하는 React 내용에 설정이나 오류 등에 대해 검색하면 이슈 attack을 하기 위해 많이 시간이 필요했다. 이런 부분은 점차 많이 연습해보면 적응하지 않을까 싶다.진도표에 따르면 이제 3일만 더 수강하면 강의내용과 미션은 끝나는 일정이다. 최대한 남은 시간에 React강의를 끝내고 미션 과제 구현에 집중할 계획이다. 단기간에 많은 시간과 노력을 쏟아올린 만큼 프론트엔드에서 React에 대한 부담감(?) 두려운 마음을 조금이나마 떨쳐보고자 한다.강의수강이 끝나면 지금까지 구현한 미션 과제에 대해 보완할 부분이 있는지, 그리고 지금까지 강의를 들으면서 노션에 정리해두었던 내용을 다시 한번 복습할 계획이다.마지막 남은 기간도 화이팅 해보자!!!<끝>

프론트엔드워밍업스터디3기프론트엔드2주차발자국JavascriptHTMLCSSReact

바다다다

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

수강강의: 따라하며 배우는 자바스크립트 A-ZTip: 워밍업 스터디 클럽을 참여하게 되면 [할인쿠폰]으로 강의를 할인 된 가격에 수강할 수 있다!1주차 강의 수강1주차를 되돌아 보며..이번 주 강의는 Javascript에 기본 문법부터 시작해서 중급 문법과 ES6에서 지원하는 class, promise등의 기능까지 학습하는 시간을 가질 수 있었다.강의가 단순하게 이론내용만 전달되는 것이 아니라 실제 코드를 구성하고 해당 Javascript코드가 HTML화면에서 어떤 동작을 하게되는지 실습과 함께 진행되어서 이해하기 더 쉬웠다. 물론 Javascript를 사용하는게 처음은 아니라 좀 더 여유롭게 강의를 이해할 수 있었지만, 중간중간 잊고 있었던 Javascript내용을 다시 복습하기에 좋은 시간이었다.또한 실습 과제도 강의 내용과 연관성이 있어서 단순 강의만 듣는 것보다 더 성취감을 높일 수 있었다. 강의 내용을 다시 정리해보며..(주로 개인적으로 새롭게 알게된 것, 다시 기억해야하는 것들 위주로 정리했다)var, let, const var를 왜 사용하지 말라는지 명확하게 알 수 있었다. let, const로 좀 더 관리하기 편한 코드를 작성하자.for VS forEach 사실 실제 사용할 때 그냥 편하다고 생각하고 forEach를 많이 사용했던 나의 모습이 떠올랐다. 또한 for..of도 많이 사용했었다. 성능적인 측면에서 데이터의 양이 늘어나면 성능이 떨어진다는 것을 알게되었고, 코드를 작성할 때 왜 사용하는지를 좀 더 고민 해야겠다. (절대적인 것이 아니다. 테스트 환경에 따라 결과가 다를 수 있음) CRP과정: Critical Rendering Path DOM tree 생성 → Render tree생성 → Layout → Paint DOM을 생성하는 것은 비용이 많이 발생하지 않지만, render, layout, paint에서 많은 비용이 발생한다. → 따라서 React에서는 가상의 DOM을 사용해서 최적화className VS classList className:특정 element의 클래스 속성의 값을 가져와서 설정할 수 있다.기존에 className을 전부 지우고 추가한다. classList: 읽기전용 property.classList.add()를 통해 기존 className을 유지한 채로 class명을 추가할 수 있다Event 종류가 너무 많다...이것들을 현업에서 사용가능하게 하려면? 공식 문서를 잘 찾아봐야 할 거 같다.어떤 기능을 구현하려고 하는데 이미 지원하는 메서드가 존재하지 않을까?를 생각해보자.Event Delegation (이벤트 위임):하위 요소의 공통 이벤트를 상위 요소에 위임함(강의 듣다가 신기한 코드 발견!)<div id="buttons"> <p>123</p> </div> <script> const buttonList = document.querySelector("#buttons"); buttons.addEventListener("click", () => alert("clicked")); </script> Q: 여기서 buttons는 HTML 요소의 id값 인데 js에서 그대로 사용해서 접근할 수 있다?A: 요소의 id또는 name은 window객체의 property로 추가되어 그대로 사용할 수 있지만, 다른 window객체의 property와 충돌될 수 있으니 사용하지 말자. (결론: 이렇게 쓰지마) JS에서 method VS function (이부분이 강의에서 언급되었는지는 잘 모르겠다. 강의 들으면서 궁금해서 찾아봤다) method: 호출방식, 함수를 호출하는 객체가 있는 경우, 즉 사용자가 정의한 객체의 property가 함수인 경우 해당 함수는 method라고 한다. function: 함수를 호출하는 객체가 없는 경우 Lexical this: 화살표 => 함수에서 this는 상위 스코프를 가리키게된다. bind, call, apply this를 function안에서 사용하면 window객체를 참조하게 되는데, 이를 다른 객체를 참조하도록 도와주는 함수들call(): 함수를 호출하는 함수const fullName = function(city, country){ console.log(this.firstName + ' ' + this.lastName, city, country); } const person1 = { firstName: 'John', lastName: 'Smith' } fullName.call(person1, "Oslo", "Norway") // call함수를 통해 person1 객체를 함수로 넘겨줌 apply() - 배열로 함수의 parameter를 전달함const fullName = function(city, country){ console.log(this.firstName + ' ' + this.lastName, city, country); } const person1 = { firstName: 'John', lastName: 'Smith' } fullName.apply(person1, ["Oslo", "Norway"]) // call함수와 달리 배열 형식으로 넘겨주는 방식 bind() - binding만으로 호출이안되고 새로 call을 해줘야함.function func(language){ if(language === 'kor'){ console.log(`language:${this.korGreeting}`) } else { console.log(`language:${this.engGreeting}`) } } const greeting = { korGreeting: '안녕', engGreeting: 'Hello' } const boundFunc = func.bind(greeting) //binding 만 시켜줌 따라서 새로운 값에 할당하고 호출해야함 boundFunc('eng') Javascript는 동기 언어이다. 어떻게 비동기를 실행가능한가? setTimeout()은 JS가 아니다. 브라우저에서 사용하는 것이면 브라우저 API를 사용하는 것(Window Object), Node에서 사용한다면 Node API를 사용하는 것이다(global Object) JS엔진 Memory Heap: 메모리 할당이 발생하는 곳 (변수를 정의하면 저장해두는 곳) Call Stack: 코드가 실행될 때 스택들이 쌓이는 곳 -> 여기서 이벤트 루프가 call stack, callback queue를 주시하고 있다가 call stack이 비어지면 callback queue에 대기중인 setTimeout작업을 실행하게 됨.setTimeout()을 0초로 설정하면 0초(즉시 실행)를 보장할까?Nope! 이벤트 루프를 타고 callback queue를 들렀다가 오는거니까 즉시실행은 아님.Closure다른 함수 내부에 정의된 함수(innerFunction)이 있는 경우 외부 함수(outerFunction)이 실행을 완료하고 해당 변수가 해당 함수 외부에서 더 이상 엑세스할 수 없는 경우에도 해당 내부 함수는 외부 함수의 변수 및 범위에 엑세스 할 수 있다.function outerFunction(outerVariable) { return function innerFunction(innerVariable) { console.log("Outer Variable: " + outerVariable); console.log("Inner Variable: " + innerVariable); }; } const newFunction = outerFunction("outside"); newFunction("inside"); // 여기서 outerVariable은 여전히 outside를 기억하고 있다.Map, Filter, Reduce Map(): 배열내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환함Filter(): 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환 Reduce(): 배열의 각 요소에 대해 주어진 리듀서(reducer)함수를 실행하고, 하나의 결과값을 반환한다.undefined VS null 원시 자료형 undefined타입은 undefined이 유일하고, null타입은 null값이 유일하다. undefined는 자바스크립트 엔진이 변수를 초기화할 때 사용한다. 개발자가 의도적으로 undefined를 할당하는 것은 권장하지 않는다. null: 비어있는, 존재하지 않는 값을 의미 NULL, Null과는 다르다! 변수 값이 없을 경우 명시하기 위해서는 undefined가 아닌 null을 사용한다null을 할당하면 변수가 이전에 참조하던 값을 명시적으로 참조하지 않겠다고 하는 것이므로, JS엔진이 이 변수에 메모리 공간에 대해 가비지 콜렉션을 수행한다. (garbage collection: 더이상 사용하지 않는 메모리를 자동으로 정리하는 것)얕은 비교 VS 깊은 비교숫자, 문자열 등의 원시 자료형은 값을 비교한다배열, 객체 등 참조 자료형은 값 혹은 속성을 비교하지 않고, 참조되는 위치를 비교한다.객체의 경우 깊은 비교가 필요함- 깊이가 깊지 않을 경우: JSON.stringify()- 깊이가 깊을 경우: lodash라이브러리의 isEqual()사용함수 표현식(Expression), 함수 선언문(Statement)함수표현식 let funcExpression = function() {...}함수선언문 function funcDeclaration() {...}차이: 함수 선언식은 호이스팅에 영향을 받지만, 함수 표현식은 호이스팅에 영향을 받지 않는다.  강의 내용을 들으면서 아쉬웠던 점..우선 단기간에 많은 양의 강의를 들어야해서 강의에서 진행한 실습을 전부 따라하기에는 힘들었다. 기존에 JS에 대한 기초 지식이 있어서 강의를 다 따라올 수 있었지만, 처음 스터디에 참여하신 분이라면 많이 힘들었을 수 있겠다는 생각을 했다. (특히 직장인은.. 더..)자기 주도학습인 스터디 형식이다 보니 미션 과제를 제출해서 과제 전체 내용에 대한 코드리뷰는 받기 어려웠다. 그래서 미션 과제를 진행하면서 어떤 기능을 더 추가할 수 있을까, 어떻게 하면 편의성을 높일 수 있을까를 많이 생각했던거 같다. 또한 과제 제출시간에 맞춰서 미션을 수행해야 하다보니 코드가 생각보다 깔끔하게 짜여진거 같지는 않았다. (이 부분은 추후에 시간나면 리팩토링 진행할 생각이다..)다음주는 React 프로젝트가 시작되는데,, 조금 걱정이긴하다.. 잘 할 수 있을까? 1주차 미션 완료(글이 길어져 미션에 대해서는 간략하게 시연 위주로 작성하고, 회고는 여기 링크에 따로 작성해두었다.)https://www.inflearn.com/blogs/94751주차 미션은 주로 강의내용에 기반해서 HTML, JS, CSS를 사용해서 Single-page application을 작성하는 과제들이었다. HTML, CSS는 강의에서 다루는 범위가 아니고, JS를 중점으로 다루고 있기때문에 JS의 문법 동작성을 확인하는 데에 더 초점을 두었다고 생각한다. 따라서 과제를 수행할 때 HTML, CSS는 최소한으로 간략하게 작성하고 script파일 작성에 좀 더 중점을 두었다. 제출한 미션은 Github Page를 사용해서 배포하였다.Demo: https://rim0703.github.io/React-study/Source Code: https://github.com/rim0703/React-study/tree/main/Javascript Mission1 - 음식 메뉴 앱(맥도날드를 좋아해서 맥도날드 메뉴판을 간략하게 구성해보았다. 뭐든 관심있는 주제가 더 재밌지 않은가?)Mission2 - 가위 바위 보 앱(과제 참고 영상에서는 컴퓨터에 대한 결과가 출력되지 않아 추가로 컴퓨터의 결과와 플레이어의 결과를 비교할 수 있도록 구현하였다.)Mission3 - 퀴즈 앱(오답 시 다음 퀴즈로 넘어가지 않도록 구현하였다) Mission4 - 책 리스트 나열 앱(삭제 기능 외에 수정 기능을 추가 구현하였다) Mission5 - Github Finder 앱(사용자input을 감지해서 API call하는 대신, 버튼을 통해 input을 제출하면 API call하는 방식으로 개선하였다)(예시 화면은 향로님 깃헙 ^^7) 마지막으로 1주차 회고전반적으로 확실하게 자기주도 학습이 강한 느낌을 받았다. 본인의 일정에 맞춰서 일주일 내에 강의를 수강하고, 과제를 제출하는 것이 쉬운 일이 아닌거 같다. 그럼에도 첫 주를 잘 마무리 할 수 있어서 뿌듯하다.처음에는 React에 대해 학습하고자 스터디에 참여하게 되었지만, 실제 기초적인 Javascript에 대한 내용도 잊은 부분이 많다는 것을 깨닫게되었다.진도표에 보면 다음 주에는 JS강의가 끝나고 React 강의가 본격적으로 시작된다.React 미션과제는 또 어떤 구현 내용들이 기다리고 있을까?기대반 걱정반이다.. 다음주도 화이팅해보자! <끝>

프론트엔드워밍업스터디3기프론트엔드1주차발자국JavascriptHTMLCSSReact

이수진

[인프런 워밍업 클럽 Full-Stack 3기] 1주차 발자국 - TODO List 구현

이제 막 3개월차에 들어가는 새내기 개발자. 일을 다니면서 퇴근 후 개인공부 또는 개발을 하며 멋있는 삶을 보내는 나날을 상상했지만 현실은 퇴근하면 누워있기 바쁜 그런 회사원A. 그러던 차에 인프런에서 스터디를 한다는 소식을 들었다.이런 프로젝트가 있다는 것은 이미 알고 있었지만, 이전에는 이미 들은 강의이거나 기술이 맞지 않아 참여를 하지 않았었는데 마침 관심있던 Supabase와 요즘 실무에서도 사용중인 Next.js를 결합한 프로젝트 강의라니! 심지어 할인된 가격에! 이건 안할 수 없겠다 싶어서 바로 신청했다. 솔직히 사놓고 안듣고있는 강의가 넘치는데 이런 프로그램에라도 참여해야 강의를 완강할 수 있겠다 싶은 생각도 있긴했다. ㅠㅠ 수강 내용Section 1, 2 : OT 및 기술 소개Section 1과 2에선 전체 프로젝트에 대한 소개와 사용 기술에 대한 설명이 주를 이뤘다.이번 강의로 다음과 같은 기술 스택들을 배울 수 있다고 했다.Supabase에서는 Storage, Database, Auth, Realtime 등 Next.js 14, Typescript, Tailwind CSS, Meterial UI Tailwind, Recoil, Tanstack QueryAWS, Vercel, GoDaddy(Domain) 확실히 기존에 개인 프로젝트를 할 때 Firebase를 사용했었는데 Firebase같은 경우는 나온지 오래되서 그런지 레퍼런스들이 굉장히 많았다면 Supabase는 그런 점이 좀 부족하다 생각했었는데 강의로 그런 부분들을 채울 수 있어서 좋았다. 실제로 혼자 한번 Supabase를 설정하려고 했었는데 안되서 한참 찾아봤던 문제를 강의에서 짚어주신 부분도 있었다.강의에서도 좀 중요하게 강조했던 부분이 있다면 Tanstack Query가 아닐 까 싶다. 사실 좋다 좋다 해서 사용하지 왜 좋은지 왜 사용하는지 잘 알지 못하고 썼었는데 이번 기회에 왜 사용하는지, 어떻게 사용하는지 좀 더 잘 알게 된 것 같아서 나중에 효율적으로 사용할 수 있을 것 같다는 생각이 들었다.Tanstack Query (React Query)서버와 통신할 때 관리해야 하는 상태들이 많이 다양한데, 이런 상태 관리를 클라이언트에서 사용하는 데 도움을 주는 라이브러리.fetching, caching, 서버 데이터와의 동기화를 지원해주는 라이브러리캐싱 : 캐싱을 통해 동일한 데이터에 대한 반복적인 비동기 데이터 호출을 방지하고, 불필요한 API 콜을 줄여 서버에 대한 부하를 줄이는 결과를 갖는다.Client 데이터와 Server 데이터 간의 분리 : 프로젝트 규모가 커지고 관리해야 할 데이터가 넘치다보면 클라이언트에서 관리하는 데이터와 서버에서 관리하는 데이터가 분리될 필요성을 느낀다.서버에서 가져오는 데이터 : react-query 클라이언트에서 관리하는 데이터 : recoil, zustand ...useQuery : 데이터를 가져오는 작업에 사용useMutation : 데이터를 변경하는 작업(PUT, POST, DELETE)에 사용queryClient.invalidateQueries : queryClient를 사용해 쿼리 요청을 다시 진행할 수 있다. (다른 곳에서 refetch를 진행하기 위함Section 3. TODO LIST 만들기(UI 구현은 블로그 글로는 생략...)본격적으로 프로젝트를 만들어보는 시간이다. Supabase에는 Table Editor, SQL Editor, Database, Authentication, Storage 등 어떤 기능들이 있는지 각각 자세하게 설명해주셨다.그리고 Supabase를 사용하기 위한 환경변수를 설정하고, Supabase Database를 사용할 때 쉽게 types를 추가하기 위해 script를 추가했는데 처음엔 몰랐는데 나중에 미션을 진행하다보니 자동으로 supabase에 있는 table에 대한 타입을 추가해준다는게 굉장히 편리했다."scripts": { // dev, build, start, lint "generate-types": "npx supabase gen types typescript --project-id [project_id] --schema public > types_db.ts" }그리고 이제 Supabase 사용하기 위한 client server middleware 설정도 진행했다. 혼자 공식문서를 봤으면 한참 해멨을 텐데 간단하게 설정이 완료됬다. 이 부분은 나중에 supabase로 개인 프로젝트를 진행할 때 요긴하게 사용할 듯.이제 Next.js Server Action을 구현했다.server action : api 구현 없이 함수 호출만으로도 api를 대신할 수 있는 기능getTodo, createTodo, updateTodo, deleteTodo를 만들었다. 그 중 getTodo와 createTodo를 보면 다음과 같다.export async function getTodos({ searchInput = "" }): Promise<TodoRow[]> { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase .from("todo") .select("*") .like("title", `%${searchInput}%`) .order("created_at", { ascending: true }); if (error) { handleError(error); } return data; }from : 가져올 Table 선택 select : 가져올 부분 선택like : title에 searchInput이 앞뒤로 들어가 있는 부분이 있다면 모두 가져온다.order : 정렬export async function createTodo(todo: TodoRowInsert) { const supabase = await createServerSupabaseClient(); const { data, error } = await supabase.from("todo").insert({ ...todo, created_at: new Date().toISOString(), }); if (error) { handleError(error); } return data; }created_at : 사용자가 잘못 입력할 수도 있어 데이터가 생성될 때 서버에서 자동으로 날짜가 생성되도록 구현actions들도 모두 구현했으니 이제 DOM에 이벤트를 달아줄 일만 남았다. 위에서 Tanstack Query를 사용하는 방법을 배웠듯 서버에서 데이터를 가져오는 것은 useQuery를 사용하고 데이터를 생성, 수정, 삭제하는 부분은 useMutation을 사용해 구현할 것이다.const { data, isLoading, refetch } = useQuery({ queryKey: ["todos"], queryFn: () => getTodos({ searchInput }), });const { mutate, isPending } = useMutation({ mutationKey: ["todos"], mutationFn: () => createTodo({ title: "New Todo", completed: false, }), onSuccess: () => refetch(), });useQuery useMutation을 담당하는 부분들의 일부를 한번 가져왔다. 이렇게 데이터를 get 할 때는 useQuery 그리고 create 등을 할 때 useMutation을 사용했다.강의를 모두 마치면 CRUD 기능을 모두 할 수 있는 TODO List가 완성된다.미션1주차 미션은 "생성 시간과 완료 시간"을 표시하는 것.생성 시간은 이미 존재하므로 완료 시간만 Supabase Todo Table에 completed_at 컬럼을 추가했다. 이 때 아까 위에서 말했듯 generate script를 통해 쉽게 table type을 정의할 수 있어서 간편했다.actions에 updateTodo 부분에 completed_at을 추가하고 todo.completed가 true일 경우 새 날짜를 작성하도록 구현했다.const { data, error } = await supabase .from("todo") .update({ ...todo, updated_at: new Date().toISOString(), completed_at: todo.completed ? new Date().toISOString() : null, }) .eq("id", todo.id ?? 0);그리고 시간을 표시하기위해 Date를 바꾸는 함수를 추가했다. (YYYY-MM-DD HH-MM-SS 형식)export const formatDate = (dateString: string) => { if (!dateString) return ""; const date = new Date(dateString); const formattedDate = date.toLocaleDateString("en-CA"); const formattedTime = date.toLocaleTimeString("en-GB", { hour12: false, }); return `${formattedDate} ${formattedTime}`; // 이부분 ${} 이 연속된 부분인데 현재 코드 블럭에서 이상하게 나온다. };따라서 생성 날짜와 수정된 날짜를 표시하면 다음과 같이 결과물이 완성된다.마무리 직장과 병행하며 프로그램을 수행한다는 것이 조금 어려웠지만 무사히 1주차 미션들을 달성해서 뿌듯하다. 혼자라면 중간에 포기하거나 조금 루즈해지거나 했을텐데 다들 열심히 사는 것 같아 나도 뒤쳐지지 않도록 노력했다.개인적으로 이번 강의를 모두 마치고 목표가 있다면 Supabase + Next.js를 통해 나만의 블로그를 만드는 것. 강의를 들으면서 나중에 이 기능은 어떻게 활용하면 좋겠다라는 상상도 해보고 하는 시간들이 즐거웠다. 또 강사님께서 차근차근 친절히 알려주셔서 더 쉽게 학습됬었던 듯 하다. 개인적으로 supabase이외에도 react query를 좀 더 상세하게 알 수 있었던 시간이라 (위에서도 말했듯 이전에 사용할 때에는 그냥 좋다니까 썼지 제대로 쓴다는 느낌은 받지 못했다) 굳이 fullstack 스택이 아니더라도 front-end 개발자 입장에서도 한단계 스킬 업한 느낌이었다.앞으로 3, 4주차에 좀 타이트하게 진행된다고 했는데 이번주는 스케쥴에 딱 맞게 진행해서.. 다음주에는 조금 더 신경써서 시간을 할애해봐야겠다. 스터디원들 모두 화이팅...! ❣

웹 개발웹개발풀스택SupabaseReactNext

치현

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

따라하며 배우는 자바스크립트 A-Z미션미션1(Day 2) (음식 메뉴 앱)깃허브 저장소 주소: https://github.com/kelvin-chihyun/inflearn-js-react/tree/main/js/food-menu미션2(Day 3) (가위바위보 앱)깃허브 저장소 주소: https://github.com/kelvin-chihyun/inflearn-js-react/tree/main/js/r-p-s미션3(Day 4) (퀴즈 앱)깃허브 저장소 주소: https://github.com/kelvin-chihyun/inflearn-js-react/tree/main/js/quiz미션4(Day 5-1) (책 리스트 앱)깃허브 저장소 주소: https://github.com/kelvin-chihyun/inflearn-js-react/tree/main/js/book-list미션5(Day 5-2) (깃허브 파인더 앱)깃허브 저장소 주소: https://github.com/kelvin-chihyun/inflearn-js-react/tree/main/js/github-finder회고굉장히 짧은 시간 내에 많은 내용을 독파해야 하는 커리큘럼인 것 같다. 다시 들어야할 부분이 적지 않았다..JS를 어느 정도는 알고 있지만서도, 많은 양을 한꺼번에 다시 보려니까 정말 많은 시간을 들여야했던 것 같다.(남은 3주,, 험난함이 가득한 여정이 예상된다...🥹) FE 워밍업 클럽에서 개인적으로 얻고자 하는 것은, 강의를 듣기 전 보다 조금 더 나아진 기본기이다.(Event Loop, OOP, Design Pattern을 다시 훑고 싶기도 했다.) 리액트 사용에만 익숙해져서 바닐라 자바스크립트로 querySelector나 addEventListner, appenChild, createElement 등의 기본 메서드가내가 알던 게 맞는지 반신반의하며 오랜만에 MDN 공식문서를 순회하기도 했다. 남은 JS도 마무리 잘하고, 이해도가 전보다 높아진 상태에서 React Core에 대한 내용을 깊게 파고 이해하고 싶다. 

프론트엔드프론트엔드인프런워밍업스터디클럽3기JavaScriptReactFE1주차발자국

치현

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

[풀스택 완성] Supbase로 웹사이트 3개 클론하기 (Next.js14)학습 내용 섹션 1. 오리엔테이션 ~ 섹션 3. 연습 프로젝트 - TODO LIST 만들기.이번 주는 라이브러리(or 프레임워크) 간략 소개 및 기본 문법이 주를 이룬다고 할 수 있다.라이브러리 사용법은 공식문서나 강의노트를 참고하면 좋을 것 같고,이 라이브러리(or 프레임워크)를 왜 사용하는 지를 적어두려고 한다. SupabaseSupabase는 위와 같은 장점과 단점을 가진다고 한다. 장점1. 오픈소스 프로젝트 (자체 서버구축 가능)완전한 오픈소스로 제공되어 무료로 사용 가능자체 서버에 직접 설치하고 운영 가능Docker를 통한 쉬운 배포와 구성커뮤니티 기반의 지속적인 발전과 버그 수정기업의 보안 정책에 맞춰 커스터마이징 가능2. PostgreSQL 기반 (관계형 DB 장점을 살릴 수 있다)강력한 PostgreSQL의 모든 기능 사용 가능복잡한 관계형 데이터 모델링 지원트랜잭션 및 ACID 속성 보장강력한 쿼리 최적화와 인덱싱풍부한 데이터 타입과 확장 기능외래 키 제약조건을 통한 데이터 무결성 보장3. Firebase 대비 저렴무료 티어의 관대한 제공사용량 기반의 합리적인 가격 정책자체 호스팅 시 비용 절감 가능예측 가능한 비용 구조과금 체계의 투명성불필요한 기능에 대한 과금 없음4. 다양한 연동방식 지원 (+ GraphQL, API, SDK, DB Connection)RESTful API 자동 생성GraphQL 인터페이스 기본 제공다양한 프로그래밍 언어를 위한 공식 SDK 제공JavaScript/TypeScriptPythonDart/Flutter기타 다양한 언어실시간 데이터 구독 기능직접적인 데이터베이스 연결 지원OAuth 및 소셜 로그인 통합자동 생성된 API 문서 단점. 아직 성숙하지 않은 커뮤니티 기반Firebase 대비 작은 커뮤니티 규모상대적으로 적은 레퍼런스와 예제 코드문제 해결을 위한 자료 부족2. 비교적 적은 기능들, 적은 서비스 연동 지원Firebase 대비 제한된 기능 세트서드파티 서비스 통합 옵션 부족일부 고급 기능 미지원3. 부족한 문서화, 한글 문서 부족불완전하거나 업데이트가 늦은 문서한국어 문서의 절대적 부족일부 기능에 대한 설명 부실4. Firebase보다 높은 러닝커브PostgreSQL 지식 필요초기 설정의 복잡성상대적으로 더 많은 학습 시간 요구Next.js Next.js는 내용이 방대하기에 정리라기보다는 공식문서의 Next.js에 대한 소개로 대체하겠다.Next.js란?풀스택 웹 애플리케이션을 구축하기 위한 React 프레임워크입니다. React의 기본 기능에 추가적인 최적화와 기능을 제공하며, 개발자가 복잡한 설정 대신 애플리케이션 개발에 집중할 수 있게 해줍니다.주요 기능1. 라우팅 시스템파일 시스템 기반 라우터레이아웃, 중첩 라우팅 지원로딩 상태, 에러 처리 기능2. 렌더링클라이언트/서버 사이드 렌더링정적/동적 렌더링Edge와 Node.js 환경에서의 스트리밍3. 데이터 페칭서버 컴포넌트에서 async/await 사용요청 메모이제이션데이터 캐싱과 재검증4. 스타일링CSS ModulesTailwind CSSCSS-in-JS 지원 5. 최적화이미지 최적화폰트 최적화스크립트 최적화6. TypeScript향상된 타입 체크효율적인 컴파일커스텀 TypeScript 플러그인라우터 종류App Router: 최신 라우터(v13.4.0 이후)서버 컴포넌트스트리밍 지원최신 React 기능 활용Pages Router: 기존 라우터서버 사이드 렌더링기존 프로젝트 지원안정성 검증됨 Tailwindcss유틸리티 우선(utility-first) 방식의 CSS 프레임워크로, 미리 정의된 클래스들을 조합하여 스타일링하는 방식을 제공한다.// 유틸리티 우선 접근 <!-- 기존 CSS 방식 --> <div class="chat-notification"> <p class="chat-title">새 메시지</p> </div> <!-- Tailwind 방식 --> <div class="flex items-center p-4 bg-white rounded-lg shadow-md"> <p class="text-lg font-semibold text-gray-700">새 메시지</p> </div>장점빠른 개발: 클래스 이름을 고민할 필요 없음일관성: 미리 정의된 디자인 시스템 제공커스터마이징: 설정 파일을 통한 쉬운 확장번들 크기: 사용한 클래스만 포함되어 최적화됨반응형 디자인: 내장된 반응형 클래스 제공단점긴 클래스명초기 학습 곡선 RecoilFacebook에서 개발한 React 전용 상태 관리 라이브러리(23년 이후로 깃허브 저장소가 업데이트되고 있지 않는 이슈가 있어, 다른 라이브러리가 권장된다...)주요 특징React 전용으로 설계된 상태 관리 도구간단하고 직관적인 API비동기 데이터 처리 지원상태 파생과 캐싱 기능TypeScript 지원1. Atom - 기본 상태 단위// atoms/todoAtom.ts import { atom } from 'recoil'; // 할 일 목록 상태 export const todosState = atom({ key: 'todosState', default: [], }); // 필터 상태 export const todoFilterState = atom({ key: 'todoFilterState', default: 'all', // 'all' | 'completed' | 'uncompleted' });2. Selector - 파생 상태// selectors/todoSelector.ts import { selector } from 'recoil'; import { todosState, todoFilterState } from '../atoms/todoAtom'; export const filteredTodosState = selector({ key: 'filteredTodosState', get: ({get}) => { const filter = get(todoFilterState); const todos = get(todosState); switch (filter) { case 'completed': return todos.filter((todo) => todo.completed); case 'uncompleted': return todos.filter((todo) => !todo.completed); default: return todos; } }, }); export const todoStatsState = selector({ key: 'todoStatsState', get: ({get}) => { const todos = get(todosState); const total = todos.length; const completed = todos.filter((todo) => todo.completed).length; const uncompleted = total - completed; return { total, completed, uncompleted, percentCompleted: total === 0 ? 0 : (completed / total) * 100, }; }, });3. 컴포넌트에서 사용 예시// components/TodoList.tsx import { useRecoilState, useRecoilValue } from 'recoil'; import { todosState, todoFilterState } from '../atoms/todoAtom'; import { filteredTodosState, todoStatsState } from '../selectors/todoSelector'; export function TodoList() { // 상태 읽기와 쓰기가 모두 필요한 경우 const [todos, setTodos] = useRecoilState(todosState); const [filter, setFilter] = useRecoilState(todoFilterState); // 읽기만 필요한 경우 const filteredTodos = useRecoilValue(filteredTodosState); const stats = useRecoilValue(todoStatsState); const addTodo = (text: string) => { setTodos([ ...todos, { id: Date.now(), text, completed: false, }, ]); }; const toggleTodo = (id: number) => { setTodos( todos.map((todo) => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) ); }; return ( <div> {/* 필터 컨트롤 */} <div> <select value={filter} onChange={(e) => setFilter(e.target.value)}> <option value="all">모두 보기</option> <option value="completed">완료된 항목</option> <option value="uncompleted">미완료 항목</option> </select> </div> {/* 통계 표시 */} <div> <p>전체: {stats.total}</p> <p>완료: {stats.completed}</p> <p>미완료: {stats.uncompleted}</p> <p>완료율: {stats.percentCompleted.toFixed(1)}%</p> </div> {/* 할 일 목록 */} <ul> {filteredTodos.map((todo) => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => toggleTodo(todo.id)} /> <span>{todo.text}</span> </li> ))} </ul> </div> ); }React Query(Tanstack Query)서버 상태 관리를 위한 강력한 라이브러리로, 데이터 페칭, 캐싱, 동기화, 업데이트를 효율적으로 처리1. 기본 사용법(Query)// 기본적인 쿼리 사용 function TodoList() { const { data, isLoading, error } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos, }); if (isLoading) return <div>로딩 중...</div>; if (error) return <div>에러 발생!</div>; return ( <ul> {data.map(todo => ( <li key={todo.id}>{todo.title}</li> ))} </ul> ); }2. 데이터 변이(Mutation)function AddTodo() { const mutation = useMutation({ mutationFn: (newTodo) => axios.post('/todos', newTodo), onSuccess: () => { // 캐시 무효화 및 쿼리 다시 가져오기 queryClient.invalidateQueries({ queryKey: ['todos'] }); toast.success('할 일이 추가되었습니다!'); }, }); return ( <button onClick={() => mutation.mutate({ title: '새로운 할 일' })}> 할 일 추가 </button> ); }3. 자동 캐싱 및 재검증// 캐시 시간 및 재시도 설정 const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos, staleTime: 5 * 60 * 1000, // 5분 cacheTime: 30 * 60 * 1000, // 30분 retry: 3, // 실패시 3번 재시도 });4. 의존적 쿼리function UserTodos({ userId }) { // userId가 있을 때만 쿼리 실행 const { data: todos } = useQuery({ queryKey: ['todos', userId], queryFn: () => fetchUserTodos(userId), enabled: !!userId, }); }5. 무한스크롤 / 페이지네이션function InfiniteTodos() { const { data, fetchNextPage, hasNextPage, isFetchingNextPage, } = useInfiniteQuery({ queryKey: ['todos'], queryFn: ({ pageParam = 0 }) => fetchTodoPage(pageParam), getNextPageParam: (lastPage) => lastPage.nextCursor, }); return ( <div> {data.pages.map((page) => ( page.todos.map(todo => <TodoItem key={todo.id} todo={todo} />) ))} <button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage} > {isFetchingNextPage ? '로딩 중...' : hasNextPage ? '더 보기' : '더 이상 없음'} </button> </div> ); }장점자동 캐싱중복 요청 방지 데이터 신선도 관리메모리 효율적 관리서버 상태 동기화백그라운드 업데이트 자동 재시도에러 처리개발자 경험DevTools 제공TypeScript 지원직관적인 API성능 최적화 자동 가비지 컬렉션요청 중복 제거스마트 리페칭단점학습 곡선복잡한 개념들 (staleTime, cacheTime, invalidation 등)최적의 설정을 위한 깊은 이해 필요다양한 옵션들로 인한 초기 진입 장벽번들 크기기본 번들 사이즈가 큰 편 (약 12KB gzipped)작은 프로젝트에서는 과도할 수 있음 보일러플레이트 반복적인 쿼리 설정 코드전역 설정을 위한 추가 코드 필요TypeScript 사용 시 타입 정의 부분이 길어질 수 있음상태 관리 한계 클라이언트 상태 관리에는 부적합서버 상태 전용이라 별도의 상태 관리 도구 필요때로는 다른 상태 관리 라이브러리와 함께 사용해야 함캐시 관리 복잡성 복잡한 캐시 무효화 로직캐시 키 관리의 어려움잘못된 설정 시 메모리 누수 가능성미션 1TODO LIST 중 TODO 항목 옆 생성 시간 표시(선택) completed_at 필드를 추가하여 완료한 시간도 함께 저장1번은 수업중 todo 테이블을 만들었을 때 created_at을 받아와서 배치만 하면 끝이고,2번은 edit table을 해서 completed_at column을 추가하여 배치하면 사실상 끝이었다. 깃허브 저장소: https://github.com/kelvin-chihyun/next-inflearn/tree/main/apps/todo 회고사실 1주차는 맛보기와 같다.Next.js의 기본적인 기능(라우팅, 렌더링, 데이터 페칭 등)에 대해서는 알고 있는 편이라, 복습하는 느낌으로 보았다. 이번 풀스택 워밍업 클럽에서 개인적으로 얻고자 하는 것은 강의 내용은 베이스로 두고, 다른 기술을 사용한다거나 포인트를 두는 것이다.(강사님이 잘 가르쳐주시는 것은 당연하지만, 나 자신이 수동적으로 임하면 제대로 안할 것 같았다.) 이번 주에는 아직 한번도 사용해보지 않고 알고만 있던 Turbo Monorepo로 프로젝트를 구성하는 것을 목표로, TO DO LIST를 만들기보다는 모노레포 구성에서 나오는 여러 경로 인식 에러들을 처리하는 데에 힘을 썼던 것 같다. 배포는 어떻게 해야할지 아직 감도 안잡히지만, 이어지는 프로젝트를 수행하면서 배포도 해보려고한다. 구성 방법에 대해 이해가 된 건 아니지만, 어찌됐든 구성 자체는 의도대로 만들어서 뿌듯한 이번 주였다. (투두리스트의 기능이 다소 없는 것 같아 아쉬운 부분이 있는데, 이 부분은 추후에 디벨롭해보려고 한다.)

풀스택풀스택인프런워밍업스터디클럽3기NextSupabaseReact프론트엔드1주차발자국

채널톡 아이콘