블로그

[인프런 찍먹클럽] 언리얼엔진5 스파르타 클래스 - 심화편 후기

언리얼 엔진5를 공부를 하다보면, 영어 강의들을 보게 된다. 수많은 영어 강의들한국어 강의를 찾던 중, 인프런의 언리얼엔진5 스파르타 클래스 - 심화편을 알게 되었다.심화편이라.. ㄷㄷ 벌써부터 궁금해지지 않나?! 강사님은 YAL 선생님!실전, 심화로 나누어 2개의 강의를 제작하셨다. 실전편은 무료이니 모든 분들이 체험할 수 있다. 언리얼엔진5 스파르타 클래스 - 심화편 : https://bit.ly/3T7YBbd 특히, 나는 애니메이션을 더욱 알고 싶어서 이번에 참가하게 되었다.언리얼 엔진의 애니메이션의 다양한 방식들을 알 수 있다. IK,FK를 보면 무슨 생각이 들까요? 피하고 싶어진다...😥하지만, 강의를 들으면서 애니메이션에 사용되는 용어들을 쉽게 알 수 있었다. IK: Inverse Kinematic 축을 활용해 bone을 움직임FK: 관절 인형 처럼 관절들을 조종해서 움직임을 구현출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅  리타게팅! 애니메이션을 다른 캐릭터에 복사를 하는 것이다. 당연히 그대로 사용하면 bone의 사용방식이 다를테니 망가진다. 동기화를 해주기 위해서 사용하게된다. 1. 먼저 리타겟팅을 위해 bone 체인을 설정해준다.출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅2. 리타겟팅 에셋에서 Source IK Rig와 Target Ik Rig를 비교하면서 오차들을 수정하며 동기화 시킨다.출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅 3. 리타겟한 결과를 볼 수 있다.https://youtu.be/b9l-7svKbP4출처: 언리얼 엔진5 스파르타 클래스 - 심화편, 섹션 8. UE5 리타게팅 이 방식들을 강의를 들으면서 따라하면 쉽게 제작이 가능하다.중요한 사실은 이 강의를 기반으로 자신의 애니메이션을 제작하는데 활용 할 수 있다. 물론, 강의를 따라해도 안되는 경우들이 생긴다. 나도 물론 생겨서 당황스러웠지만, 커뮤니티 게시판으로 해결을 하였다.언리얼 엔진5 스파르타 클래스 - 심화편 커뮤니티 게시판여기 게시판을 활용해서 선생님께 직접 질문을 하고 답변을 받을 수 있으니, 걱정 안해도 될 것이다. 후기나는 이번 찍먹클럽을 통해서 애니메이션에 대한 두려움이 사라지게 되었다. 배운 것들을 기반으로 나의 프로젝트에서 애니메이션을  활용할 수 있음을 확인하여 나에게 만족스러운 강의가 되었다. 이 강의를 자신이 들어도 괜찮을까 고민 하는 분들!난이도가 매우 쉽게 구성되어 있고 커리큘럼이 단계적으로 잘 구성되어 있다. 선생님과 함께 따라서 제작한다면 무리가 없을 것이다.언리얼 엔진5 에디터도 무려 한국어로 되어 있어, 영어가 어려워 접근하기 쉽지 않은 분들도 할 수 있는 쉬운 접근 중 하나가 될 것이다. 모두 스파르타 클래스 - 심화편을 듣고 언리얼 엔진5를 마스터 할 수 있도록 노력하자! 화이팅!

게임 프로그래밍인프런인프런강의후기게임개발게임개발강의인강후기강의후기게임개발자인프런강의

sql - section 1

관계형 DBOLTP (Online Transaction Processing): 트랜잭션을 하기위한 데이터베이스특징보류, 중간 상태가 없어서 데이터의 무결성을 유지할 수 있다.데이터 추가, 변경이 많다.쿼리 속도가 느리다.트랜잭션: 데이터베이스의 상태를 변화시키기 위해 수행되는 작업의 단위관련 DBMSoracle데이터 베이스 시장 1위높은 안정성과 유지보수 보장비싼 가격mysql오픈소스데이터 베이스 시장 2위postgreSQL오픈소스mysql보다 sql 표준을 잘 지원하며, 쿼리가 복잡해질 수록 성능이 더 잘 나옴mssql대규모 엔터프라이즈 수준의 시스템에 적합주로 윈도우 환경에 사용default isolation level이 read committed데이터를 읽을 때 공유잠금이 유지 -> WITH(NOLOCK)을 통해 공유잠금없이 데이터 조회OLAP(Online Analytical Processing): 데이터 웨어하우스를 이용해, 분석질의를 처리 목적으로 만들어진 데이터베이스쿼리 속도가 빠른 편데이터 웨어하우스: 분석가능한 정보의 중앙 라포지토리관련 DBMS빅쿼리구글 클라우드의 OLAP + data warehouse 사용컴퓨팅 레이어와 스토리지 레이어 분리각 레이어가 다른 레이어에 영향을 안 미침비관계형 DB (NOSQL)특징key - value 형식을 지원PK,FK JOIN을 지원하지 않음스키마에 대한 정의가 없음장점대용량 데이터 처리에 유리관계형 데이터베이스보다 읽기, 쓰기 속도가 빠름데이터 모델링에 유리분산처리에 유리데이터의 일관성을 보장하지 않아도 되고, join 연산이 필요없을 때 사용하면 유리관련 DBMS몽고DBRedis  

데이터베이스

코딩웍스(Coding Works)

[Tailwind CSS] CLI설치 VS Code에서 npm 에러 환경 변수 설정

[Tailwind CSS로 개발자가 만드는 멋진 UI 스타일링] 강의 중 [섹션 3. Tailwind CSS CLI 환경 구축]에서 [Tailwind CSS에서 CLI 환경 구축(ft. Node.js)] 영상 참고자료입니다.비주얼스튜디오코드 커멘트 창에서 npm init을 실행하면 'npm'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. 라는 오류 메세지가 나올 수 있습니다. 이건 비주얼스튜디오코드에서 발생하는 오류인데 npm의 환경 변수를 설정해주면 해결되니 오류가 나면 아래처럼 해주시면 됩니다.1) 윈도우 검색창 또는 제어판에서 환경 변수를 검색하고 시스템 환경 변수 편집 클릭※ [환경변수]라고 붙여서 검색하지 마시고 [환경 변수]라고 띄어쓰기로 검색해야 제대로 나옵니다.2) 시스템 속성 → 고급 → [환경 변수] 클릭3) 시스템 변수 항목에서 Path를 찾아 클릭 후 편집 클릭4) 새로 만들기 → C:\Program Files\nodejs\ 입력 → 확인 클릭5) VS Code 재실행 후 커멘트 창에서 npm init 다시 실행아래 화면처럼 node.js가 정상적으로 설치되기 시작하며 오케이!![Tailwind CSS로 개발자가 만드는 멋진 UI 스타일링]✅ 강의소개 및 무료강의 보러가기 : https://inf.run/pjqKk

웹 퍼블리싱tailwindcli환경설정

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

라이프웨어

전 세계 몸값 1위 오타니를 만든 비결, '좋은 루틴 만들기와 지속하기'

10년간 연봉 9천200억원,전 세계 스포츠에서 최고의 몸값을 자랑한 '오타니'와 다저스의 계약액입니다.오타니는 이미 살아 있는 전설로 불리고 있습니다.타자와 투수 어느 하나의 최고 기량을 보여주는 것도 어려운데, 오타니는 경기에서 양 포지션 모두 최고의 모습을 보여주기 때문입니다.  오타니가 이렇게 성공했던 비결은 무엇일까요?https://www.youtube.com/watch?v=xAn5z9_AfZgSource일본 야구계에 몇 차례 책을 썼던 로버트 위팅은 '오타니는 오직 역사상 최고의 야구 선수가 되는 것에만 관심을 두는, 전사로 변신한 수도승과 같다. 목표를 위해 필요한 활동들에만 정진한다'라는 평을 남겼습니다.오타니의 이 남다른 모습은 고1로 거슬러 올라갑니다. 당시 오타니는 '8구단 드레프트 1순위'라는 목표와 이 목표를 달성하기 위한 64가지 루틴을 수립했습니다. 그리고 현재까지 실천을 이어오고 있습니다.Source: 스포츠닛폰, 오타니 쇼헤이의 만다라트 목표  오타니의 비결은 세가지로 압축됩니다.1. 장기 목표를 수립합니다. 2. 목표에 효과적인 루틴을 수립합니다.3. 꾸준히 실천합니다. '장기 목표를 수립하는 방법과 워크북'에 대해선 '[기획물] 아직 꿈과 목표를 설정 못하셨나요? 이 글 하나로 끝내세요.'를 참고해 주세요. 이번 편에선 두번째 루틴 수립법과 세번째 루틴 실천법을 다룹니다.  효과적인 루틴 수립 방법첫째, 장기 목표에 효과적인 루틴을 수립합니다.장기 목표에 부합한 루틴을 수립하는 이유는 첫번째로 꾸준한 실천을 보장하는 동기부여를 얻을 수 있기 때문입니다. 두번째로 목표와 연관된 루틴을 떠올리기 쉽고, 장기 목표가 루틴의 우선순위를 판단할 기준이 되어주기 때문입니다.Source: LifeWare 장기 목표에 부합한 루틴을 수립하는 방법으로는 '만다라트 기법'을 활용하거나 혹은 '장기목표, 비전보드, 자기암시문'의 항목을 하나씩 살피면서 연관된 루틴을 뽑고 이후 루틴들의 우선순위를 설정할 수 있습니다.(참고: 만다라트 기법의 정의와 실천 방법, 쉽게 말해 정중앙에 최종 목표(인생 한문장)을 두고 마인드맵 그리듯이 연관 습관들을 뽑아내는 아이디어 도출방식입니다.) 둘째, 벤치마킹을 활용합니다.이는 비슷한 관심사를 가진 사람들의 루틴을 참고하는 방법입니다. '타이탄의 도구들' 처럼 위인들의 루틴을 모아둔 책을 참고하는 방법도 있습니다. 혹은 '챌린저스'과 같은 루틴앱에서 내게 맞는 루틴을 발견하는 방법도 있습니다.루틴 템플릿(링크)에는 루틴 도서와 앱들을 참고하여 가능한 모든 형태의 루틴들의 리스트를 뽑고 분류해 두었습니다. 필요한 루틴들은 여기서 발견하여 수립할 수도 있습니다.Source: LifeWare 라이프 시스템 - 루틴 시스템 - 루틴 아이템 모음   꾸준한 루틴 실천 비법 3단계사실 루틴을 세워도 작심삼일하기 일쑤이지 않으셨나요? 그렇다면 '루틴 실천 방법'을 제대로 적용하고 있지 않았을 가능성이 큽니다. 이 문제는 루틴이 실행되는 단계(실행 타이밍 인지> 실행 행동 발상 > 실행 동기 판단)를 구분하여 하나씩 방법을 찾을 수 있습니다.Source: LifeWare 라이프시스템 - 루틴 시스템 - 템플릿첫번째 단계는 루틴의 '수행 타이밍'을 인지하는 것입니다.Source: LifeWare, 조건 -> 행동 관계 루틴은 '조건'과 '행동'의 쌍으로 구성되어 있습니다. 따라서 루틴 실패의 첫번째 이유는 '조건'이 명확하지 않았거나 혹은 이를 인지하지 못했을 때 발생합니다.'조건'은 크게 '시간 조건'과 '상황 조건'으로 구분합니다. '우리가 매일 x시 ~를 하겠어'라는 것은 시점과 행동을 연결시킨 '시간 조건'의 사례에 해당합니다. 반면, '나는 사람들과 대화를 하는 상황에선 ~를 하겠어'라는 것은 내가 처한 상황과 행동이 연결된 '상황 조건'의 사례에 해당합니다. Source: LifeWare 시간조건과 상황조건 예시 '시간 조건'과 '상황 조건'별로 조건을 명시하게 된다면, 해당 조건이 충족했을 때 더 잘 떠오르는 것을 느낄 수 있습니다. 또 다른 방법으로는 '알림' 등을 적극적으로 활용해 조건을 인식하는데 도움을 받을 수 있습니다. 두번째 단계는 '수행할 행동'을 인지하는 것입니다.Source: LifeWare 루틴 시스템 - 수행할 행동 인지 상승 전략루틴 실패가 발생하는 두번째 이유는 '무엇을 실천'할 것인지 잊어버리기 때문입니다. 이 경우 역시 심리적인 방법을 먼저 적용해 볼 수 있습니다. 복잡한 루틴은 일상에서 떠올리기 어렵습니다. 이때는 기억하기 용이한 형태로 재구성하는 방법을 적용할 수 있습니다. 두번째로는 기억에 대한 부담을 줄이도록 '습관 체크앱' 등의 도움을 받을 수 있습니다. 외부 툴을 사용하면 '루틴 행동'을 떠올리는 속도는 느려지나, 정확도는 향상되는 장점을 가집니다. 세번째 단계는 '동기'를 가지고 실행에 옮기는 것입니다.루틴 실패가 발생하는 세번째 이유는 '동기'가 상대적으로 부족하기 때문입니다. 즉, 루틴 수행시 '기대되는 자극'이 수행을 유발하지 못하거나 다른 것을 할 때의 '기대 자극'보다 약하기 때문입니다. 기대 자극이란 '행동을 통해 얻을 보상 및 처벌의 강도'와 '실제로 행동의 성공 가능성'이 고려된 '인지적인 동기부여의 강도'를 가리킵니다. 이를 활용해 다음 두가지 접근법을 사용할 수 있습니다.Soure: LifeWare 루틴 시스템 - 기대자극 조절 전략첫째, 루틴 수행 시 '기대 자극을 높이는 전략'입니다. 대표적으로 '장기 목표와 연결시키거나 혹은 미실시에 대한 손실을 높이는 방법'을 사용합니다.'미실시에 대한 손실을 높이는 방법'을 예로 들어 보면 '벌금을 매기거나', '그룹 수행을 통해 실시하지 않는 경우, 사회적 평판 하락'에 대한 리스크를 스스로 부과시키는 방법을 사용할 수 있습니다. 둘째, 루틴을 수행하지 않고 '다른 것을 하는 경우의 기대 자극을 낮추는 전략'입니다. 대표적으로 '최종 보상에 대한 접근성을 떨어뜨리는 방법'을 사용합니다.예를 들어, 릴스 시청 및 코인 확인 등 스마트폰 중독 증상이 있다고 합시다. 이땐 스마트폰이 잠겨서 열리지 않게하거나 물리적으로 만지기 어려운 곳에 둠으로써 스마트폰의 습관적인 사용을 낮출 수 있습니다. 즉 우리는 위에서 언급한 세가지 전략인 '수행 타이밍 인지 강화', '수행할 행동 인지 강화', '상대적인 동기 강화'를 통해 실천 가능성을 높일 수 있게 됩니다.  [정리] 단계별로 루틴 설계 방법아래에는 위에서 언급한 내용을 순서대로 따라해볼 수 있도록 정리하였습니다.1. 루틴 원페이지를 마련합니다.위 원리들이 적용된 템플릿을 활용해, 루틴을 제작하시려면 아래 다운받기를 클릭해 주세요.[ 템플릿 다운받기 >> ]  (~3/17까지 무료 제공) 2. 수행할 루틴을 기록합니다.목표를 기준으로 루틴을 뽑거나 혹은 벤치마킹 등을 활용할 수 있습니다. 그 외에도 '루틴 도서 및 앱'에 언급되었던 '루틴 전체 리스트'를 템플릿에 포함해 두었으니 참고해 주세요. 3. 루틴의 조건을 세팅합니다.루틴이 실행되는 조건을 명시합니다. '시간 조건'과 '상황 조건' 중 어디에 해당하는지 생각하고, 구체적인 조건을 명시합니다. (아래 사진에서 첫번째 칼럼)Source: Lifeware - Daily Routine 예시 4. 루틴 자체가 잘 인지되도록 만듭니다.가능하면 외부 도움을 받지 않아도 루틴이 잘 기억나도록 단순화 시키는 것이 좋습니다. 그럼에도 루틴의 정확도를 높이기 위해선 루틴 체크리스트 등의 보조 도구를 활용하는 것도 좋습니다. (위 예시 사진에서 두번째 칼럼) 5. 루틴의 기대 자극을 조절합니다.루틴이 충분히 기대자극을 불러일으키는지 검토합니다. 혹은 루틴을 방해하는 행동의 기대자극이 더 큰지 검토합니다. 보상/처벌(손실) 장치로 루틴의 기대자극을 높입니다. 그리고 방해 행동의 접근성을 떨어뜨려 기대자극을 낮춥니다. (위 예시 사진에서 세번째 칼럼)  FAQ1. 목표 및 루틴을 수립한 다음에는 어떻게 해야 할까요?다음으로는 '주기적인 피드백'을 통해 루틴을 지속적으로 업그레이드해야 합니다. 그리고 목표 중 루틴만으로 충족하기 어려운 체계적인 접근이 필요한 문제들은 프로젝트성 작업을 통해 해결합니다.1. 주기적으로 피드백을 수행합니다: Daily, Weekly 피드백을 통해 잘 지켜지지 않는 루틴들은 빠르게 확인하고 문제를 해결할 수 있습니다. 피드백한 결과를 다시 루틴에 반영하여 바로잡습니다.2. 문제해결을 통해 목표 달성을 촉진합니다: 목표에 따라서는 단계별로 체계적인 문제 해결이 필요한 경우가 많습니다. 이는 체계적인 문제 해결 방법('핵심 결과물' - '프로젝트' - '테스크')을 통해 효과적으로 수행할 수 있습니다.각 부분에 대한 상세 내용들은 추후 라이프웨어 뉴스레터를 통해 받아보실 수 있습니다. 뉴스레터를 통해 지속 가능한 성공과 성장에 도움이 되는 글들을 보내드립니다.[라이프웨어 뉴스레터 구독하기>>] 2. 루틴 관리 템플릿을 혼자 채우려니 어려워요. 어떻게 하면 좋을까요?라이프웨어에서 진행하는 '라이프웨어 올인원(All-in-One)' 프로그램을 추천드립니다. 강의도 듣고 조별 활동을 통해 실천도 하면서 4주간 나만의 라이프시스템을 완성할 수 있습니다. 이 프로그램에는 '목표, 루틴 시스템' 외에도 '피드백 시스템, 문제 해결 시스템, 문서 관리 시스템' 등 '인생을 생산적으로 살아가기 위한 필수 시스템'들을 포함하고 있습니다.이 프로그램을 3월 18일부터 4주간 온라인으로 진행될 예정입니다. 현재 3일간 모집 중이며 다음 링크를 통해 확인하실 수 있습니다.‘라이프웨어 올인원’ 1기 참여 기회 잡기 (3/13~3/15)>>  오늘의 레터를 정리합니다.1. 루틴은 꿈과 목표를 기반으로 도출되어야 한다:  꿈과 목표를 기반으로 도출된 루틴은 실행 시 동기부여가 강하다는 장점도 있으며, 지향점이 같기에 루틴 간 시너지를 창출시킬 수 있습니다.2. 루틴 실천을 위해선 '루틴 조건', '루틴 행동', '루틴 동기'를 최적화 시켜야 한다: 각 단계들이 최적화 되어 있지 않다면 '작심삼일'은 불가피하게 발생합니다. 인지적인 측면에서 솔루션과 외부 솔루션 등을 종합적으로 활용해서 실천율을 높일 수 있습니다. 위 소개된 방법을 따라할 수 있는 템플릿을 활용해 보세요. [ 템플릿 다운받기 >> ]  (~3/17까지 무료 제공)  '나만의 라이프시스템'을 함께 만들어 보는 4주간의 라이프웨어 코호트 프로그램에 참여해 보세요. 현재 1기 신청을 받고 있어요. 라이프웨어 올인원 1기는 라이프시스템 하위 시스템을 하나씩 경험하면서 삶을 변화 시킬 수 있는 프로그램으로 기획했어요. 1주차 테스크, 피드백, 루틴 관리를 시작으로 핵심 결과물과 문제 해결(프로젝트) 관리 그리고 목표 관리 등을 모두 다룰 예정이에요.프로그램을 신청을 해주세요.‘라이프웨어 올인원’ 1기 참여 기회 잡기 (3/13~3/15)>> Q. 라이프웨어는 무엇을 하는 곳인가요?삶을 변화 시키려면 무엇이 필요할까라는 고민과 연구로 이 프로그램이 탄생했어요. 자기계발 서적은 여전히 불티나게 팔리고 있지만 우리 삶은 변화가 더딘 것 같아요. 라이프웨어는 해답을 '핵심 원리가 담긴 컨텐츠와 라이프시스템'에서 찾고 있어요. 핵심 원리를 이해하고 시스템을 하나씩 적용하다보면 삶이 조금씩 변화해 가는 것을 발견하실 수 있을거에요. 그리고 꿈만 꿔왔던 목표들에 더 빠르게 가까워질 수 있을거에요.라이프웨어는 매주 뉴스레터를 통해 토픽을 하나씩 전달 드리는 것과 더불어 4주간의 집중 코호트 프로그램을 통해 시스템을 내재화하고 커스터마이징 하는 경험을 제공해 드리고 있어요. Q. 라이프웨어 코호트는 누구를 위한 프로그램인가요?자기계발 서적, 동영상 소비 대비 실제 삶의 변화가 많지 않다고 느끼는 분여러 생산성 도구를 사용하느라 불편하고 통합된 시스템을 만들고 싶은 분라이프웨어에서 소개한 컨텐츠를 혼자 적용하기에 어려움이 있고 시간이 오래 걸리는 분 Q. 프로그램의 핵심 구성은 어떻게 되나요?🙆‍♂ 적용 실습: 라이프시스템의 하위 시스템을 이해하고 실제 내 삶에 적용해 봅니다. (목표 관리, 루틴 관리, 핵심 결과물 관리, 문제 해결 관리(프로젝트), 테스크와 피드백, 지식 관리)👯‍♂ 그룹 러닝: 주 1회 온라인 그룹 러닝을 통해 자신의 시스템을 소개하고 피드백을 받습니다.🏗 DIY 제작: 스스로 노션 대시보드를 수정하는 방법을 배우고 실습합니다.📖 케이스 스터디: 라이프시스템을 성공적으로 사용하고 있는 사람들의 시스템을 분석하고 벤치마킹 포인트를 발견합니다.  라이프웨어 올인원 1기 참여 기회 잡기 (~3/15)>> 

기획 · 전략 · PM루틴생산성습관목표실천

김민구

인프런 워밍업 클럽 마지막 발자국

길다면 길고 짧다면 짧았던 약 2주간의 인프런 워밍업 클럽이 끝났다.모든 끝이 아쉽듯이 인프런 워밍업 클럽도 막상 끝이 난다고 하니 아쉽다.허나 끝은 또다른 시작이 아닌가 또다른 시작은 좋은 마무리와 함께 시작된다는 말처럼 마지막 발자국과 함께 인프런 워밍업 클럽을 잘 마무리 해보려 한다.서론이 길었다.마지막 발자국 작성을 시작하겠다. 금주는 서비스를 개발하는 것 만큼이나 중요한 개발 후 배포과정에 대해 배웠다.일별로 보자면기본적인 배포를 위한 준비AWS와 EC2 배포Spring boot 설정 , 버전업 이해하기를 배웠다. 간단한 서비스를 개발해본 적은 있지만 배포까지 해본적은 없어 배포는 어떤 과정을 거쳐 이루어지는지 궁금했는데배포의 과정을 알려주시고 배포가 왜 필요한지까지 추가적으로 알 수 있어서 정말 좋았다.배포에 관해 배웠으니 앞으론 서비스를 개발하면 개발하는 것에 그치는 것이 아니라 배포까지 해봐야겠다 (제대로 된 서비스를 개발하는게 먼저 ㅎㅎ) 추가적으로 과제에 대해 말해보자면 금주는 미니 프로젝트를 진행했는데 1단계를 마치고 2단계를 진행중에 있다.2단계는 1단계보다 어렵지만 어려운만큼 고민하는 재미와 풀어냈을 때의 쾌감이 있는 거 같다.오류가 날 때마다 그만하고 싶지만 이런 우여곡절이 나중에 다 도움이 될 것이라 생각한다 미니프로젝트는 4단계까지 존재한다.가능하면 3월달 안으론 다 풀어보고 싶다. 다 풀고 한단계 성장한 내가 될 수 있길 바란다. 마지막으로 인프런 워밍업 클럽을 진행하며 느꼈던 점을 작성하며 글을 마무리하도록 하겠다.먼저 이러한 기회를 제공해주신 인프런 분들에게 감사하다는 말을 드리고 싶다.또한 참여자들과 열정적으로 소통해주신 최태현 코치님에게도 감사하다는 말을 드리고 싶다.가벼운 마음으로 시작했던 인프런 워밍업 클럽이었는데 끝나고 돌이켜보니 내가 생각했던 것보다 훨씬 더 많은 것을 얻어가는 거 같다.지금까지 혼자 공부하며 누군가 내 방향을 잡아주면 좋겠다는 생각을 항상 했었는데 인프런 워밍업 클럽이 어느 정도 방향을 잡아준 거 같다. 인프런 워밍업 클럽이 나에게 이렇게 좋은 영향을 준 거 같이 인프런 워밍업 클럽에 참여한 분들에게도 좋은 영향을 주었길 바라며 마지막 발자국 작성을 마친다. P.S 인프런 워밍업 클럽에 참여하신 모든 분들이 잘 되시길 진심으로 바랍니다 !!   

백엔드

장서윤

[인프런 워밍업 클럽 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기

최경희

[인프런 워밍업 클럽 0기] FE 3주차

3주차 학습 요약11일차 - 리액트 테스트 경험하기'Test Driven Development' 줄여서 'TDD'라고 부르는 테스트 주도 개발에 대한 개념과 장점에 대해 알 수 있었습니다. 리액트에서 TDD를 할 수 있게 해주는 React Testing 라이브러리와 그 라이브러리와 함께 사용되는 프레임 워크인 Jest를 사용하는 방법에 대해서 배웠습니다. 그리고 TDD를 이용하여 간단한 Counter 앱을 만들고, 그 앱을 AWS의 S3를 사용하여 배포하기 위해 S3를 설정하고 파일을 업로드하여 배포하는 방법과 S3에 자동으로 앱을 배포하기 위해 Github를 설정하는 방법에 대해서도 배울 수 있었습니다. 12일차 - NextJS, 타입스크립트NextJS에 대한 정의 및 개념과 NextJS 버전 12를 사용하여 간단한 블로그 앱을 만들어 보면서 사용법을 배울 수 있었고, NextJS 버전 13으로 간단한 게시판 앱을 만들어 보면서 버전 12에서 어떻게 바뀌었는지, 버전 13의 사용법은 어떤지에 대해서 배웠습니다. 2개의 간단한 앱을 만들 때 타입스크립트를 사용하여 진행되었는데, 타입스크립트의 자료형 등 기초도 배울 수 있었습니다. 마지막으로 NextJS에 있는 Server Action에 대한 정의와 사용법에 대해서도 배웠습니다. 13일차 - 리덕스 학습하기리액트가 버전 18이 되면서 새로 생긴 기능들이 어떠한 이유로 생겼는지 그리고 어떻게 사용하는지에 대해 배웠습니다. 그리고 자바스크립트의 상태 관리 라이브러리인 리덕스를 사용하는 이유와 개념을 알 수 있었습니다. 타입스크립트와 함께 미들웨어 없이 리덕스로 Counter 앱을 만들어 보며 간단하게 사용법을 익힌 후, 리덕스 Hook과 리덕스 미들웨어 등을 사용하여 ToDo 앱을 추가하는 방법에 대해서도 배울 수 있었습니다. 마지막으로 리덕스 홈페이지에서 리덕스 툴킷을 사용하여 만든 Counter 앱의 템플릿을 타입스크립트 버전으로 설치 후, 그것을 이용하여 리덕스 툴킷의 사용법을 배울 수 있었습니다. 미션 해결 과정Day10 미션 1 : 디즈니 플러스 앱API를 사용하여, 구글 연동 로그인을 진행하고 영화 리스트와 상세 정보를 확인하고 검색도 가능한 앱 입니다.과제 영상과 전체적으로 비슷하게 만들었습니다. 모달 창 크기를 살짝 줄이고, 데이터를 불러올 때 존재하지 않는 동영상이나 이미지를 불러오게 될 때에는 동영상이나 이미지가 없다는 텍스트가 적힌 화면이 보일 수 있게 했습니다.처음에는 하나하나 알아보면서 만들려고 했지만 API 사용법도, 리액트도 잘 알지 못해 만드는 것이 어려웠습니다. 그리고 시간도 부족해서 넷플릭스 강의 코드를 많이 참고하여 만들었습니다. 넷플릭스 강의 코드를 복사한 후 수정하는 방식으로 진행했습니다. 스스로 다 하고 싶었지만 그렇게 하지 못한 것이 아쉬워서 과제 제출이 끝나고 난 후에 다시 시도해 보려고 합니다. Day10 미션 2: 포켓몬 도감 앱구글 연동 로그인을 진행하고, 포켓몬 리스트와 상세 정보를 확인하고 검색도 가능한 앱 입니다.포켓몬 API를 사용하여 현재 개발하고 있는 중 입니다. Day12 미션 : 퀴즈 앱NextJS와 타입스크립트를 사용하여 만드는 것으로, 메뉴를 클릭하면 화면이 이동하고 답을 선택하고 제출했을 때 결과를 초록색과 빨간색으로 나타내고, 간단한 시험을 보는 앱 입니다.전체적으로 비슷하게 만들었습니다. 약간의 디자인과 색을 변경했습니다.전혀 모르던 NextJS와 타입스크립트를 이용하여 만들어야 해서 어려웠습니다. 타입을 지정해야 하는 것이 익숙하지 않았고, 어떤 타입으로 지정을 해줘야 할지 몰라서 막힐 때도 있었습니다. NextJS도 잘 몰라서 페이지 이동 때를 제외하고는 거의 리액트를 사용하여 만드는 것처럼 만들었습니다. 회고리액트 과제는 전체적으로 어려워서 하나 해결하는데에도 시간이 오래걸리고 고민을 많이 했던 것 같습니다. 하지만 그렇게 진행하면서 새로운 지식을 쌓을 수 있었고 경험을 얻을 수 있어서 좋았습니다. 그리고 리액트를 어느정도 다룰 수 있다고 생각이 들면 나중에 NextJS와 타입스크립트를 공부하려고 했었습니다. 이번주 강의를 통해 간단하게 NextJS와 타입스크립트가 어떤 것인지 알 수 있게 되어 나중에 NextJS와 타입스크립트를 공부할 때 많은 도움이 될 것 같습니다.제가 이 스터디를 신청한 이유는 제 자신에게 공부를 할 수 있도록 강제성을 주기 위해서였는데, 스터디 완주 조건이 저에게 강제성을 주어 공부를 열심히 할 수 있었고 공부하는 습관을 들일 수 있게 해주었습니다. 이거 하나만으로도 스터디를 신청한 보람이 있었습니다. 하지만 아쉬운 점은 현재 만들고 있는 포켓몬 도감 앱만 완성하면 완주 조건을 달성할 수 있는데, 만드는 것이 어려워서 내일까지 완성을 할 수 있을지 모르겠다는 점 입니다...🥲 최대한 노력해서 과제 제출 날인 내일까지 완성해서 제출하는 것이 목표입니다.스터디를 시작한지 별로 안된 것 같은데 벌써 3주가 지나 스터디가 끝났다는 것이 믿기지가 않습니다. 3주 동안 공부를 하고 과제를 진행하는 것이 힘들긴 했지만, 좋은 경험과 공부하는 습관을 가질 수 있게 되어 많은 도움이 되었습니다. 다음에 또 스터디가 열리게 되면 참가하고 싶습니다.

프론트엔드

도롱이

[인프런 워밍업 클럽_0기] 3주차, 세 번째 발자국 #3

3주차 요약 : 직장인은 집중하기 힘든 코스인 걸 느꼈다..!강의 요약은 notion에 정리해두었다. https://www.notion.so/d2e9b3e27b3348abbde60994cf627ebd Day 11. 객체지향과 JPA 연관관계기존 코드를 리팩토링하면서 객체지향적인 개발이 무엇인지 조금이나마 체험할 수 있었다.객체지향의 사실과 오해에서 나왔던 '객체의 메시지'를 드디어 체험하게 되었다.JPA 연관관계 및 연관관계의 주인이 되는 기준에 대해서 알게되었다.연관관계에 주인이 아닌 쪽에는 mappedBy 옵션을 줘야한다.개념은 언뜻 알고있었지만 연관관계의 주인을 정하는 기준을 이렇게 정확하게 알게된 건 처음이다.연관관계의 주인은 Table을 보았을 때 관계의 주도권을 쥐고있는 쪽이 연관 계의 주인이 된다.연관관계 주인에 의해서 객체가 연결되는 기준이 된다.연관관계의 주인 객체에서 연결되는 다른 객체의 setter를 만들어 저장하면 데이터 저장이 정상적으로 잘 된다....!연관관계를 setter로 연결 후 Transactional이 아직 끝나지 않은 시점에서 그 반대인 객체에서 주인 객체를 getter로 가져오면 null이 반환된다. -> DB 상에서는 아직 저장이 되지 않아 데이터가 없기 때문해당 문제를 해결하기 위해서는 Setter를 한 번에 둘 다 연결해주면 된다.cascade 옵션과 orphanRemoval 옵션에 대해 알게되었다.도메인 계층에 비즈니스 로직이 들어가도 된다는 걸 알게되었다. 뭔가 도메인 계층은 깨끗한 상태로 getter와 생성자 외에는 추가적인 메서드가 있으면 안될 것 같았는데 그러지 않아도 되는 것 같다.리팩토링 후 Service 계층에서 오만가지 도메인을 불러와 직접 처리해주었던걸 리팩토링 하면서 연관 관계를 사용해 최대한 도메인들끼리 직접 협력할 수 있게 코드를 변경한 진귀한 경험을 했다. -> 객체지향의 사실과 오해..! 짱지연로딩의 개념과 다양한 옵션들에 대해 알게되었다.연관관계 사용이 100% 정답이 아닌 걸 알게되었다.비즈니스 요구사항, 기술적인 요구사항, 도메인 아키텍처 등 여러 부분을 고민해서 연관관계 사용을 선택해야 함.실무에서 경험이 쌓여야 판단이 가능할 것으로 보인다. 과제는 따로 못했다..! 야근이 날 괴롭혀서 강의도 겨우 들었기 때문....ㅜDay12. 기본적인 배포를 위한 준비배포의 개념에 대해서 알게되었다.profile 기능을 사용해서 H2와 Mysql 관련 설정을 분리하였다.git과 github의 개념과 차이점을 알게되었다.git의 기초 사용법을 다시 한 번 리마인드하는 시간이 되었다.git은 사용해봐서 기초 사용법을 알고 있는 상태였다.AWS의 EC2를 난생 처음 사용해봤다. 과제는 따로 하지 못했다.. 이날도 아마 야근했던 거 같다... 😢Day13. AWS와 EC2 배포AWS에서 리눅스 명령어를 다뤄봤다.AWS에 콘솔에 접근하는 방법을 알게되었다.리눅스 명령어 중에 권한 관련 명령어가 좀 어려운 것 같다. 추가적인 공부가 필요한듯.배포를 위한 프로그램 설치는 Java, mysql, git이었는데 mysql을 설치할때 좀 곤욕이었다. 강의 영상과 현재 ec2 리눅스 버전이 달라서 그런건지 강의에 나오는 명령어를 입력하면 에러가 나서 블로그 이것저것을 검색해봤다. 난생 처음 CLI로 빌드를해봤다. 빌드할 때도 영상을 그대로 따라서 하면 에러가 발생했다. 영상에서는 java 11을 설치했는데 내 프로젝트는 java가 17이라 java를 다시 설치해주었더니 정상적으로 빌드가 됐다.foreground와 background의 개념을 알게되었다.난생 처음 도메인을 구입해봤다.Day14. Spring Boot 설정, 버전업 이해하기build.gradle에 대해서 알게되었다.dependency configuration에 대해서 알게되었다.Spring과 Spring boot의 역사에 대해 간략하게 알게되었다.YAML 문법에 대해 알게되었다.Day15. 마무리 및 추가 꿀팁 영상공부 방향성에 대한 얘기 중 spring의 원리 및 클린 코드를 공부해야겠다고 생각했다.코틀린에 대한 관심도가 올라갔다. 조만간 코틀린에 대해서도 공부할지도?스프링 배치가 궁금해서 관련 강의를 사놨었는데 이것도 얼른 수강해야겠다.Spring boot에서 Mybatis를 사용하는 법을 알게되었다. 우리 회사 같은 경우는 옛날 기술들을 많이 쓰고 최신 기술에 대한 거부감이 있는데 이런 걸 보여줌으로써 차근차근 최신 기술로 안내하는 것도 나쁘지 않을 것 같다Client-Side Rendering과 Server-Side Rendering의 개념을 알게되었다.  완강 후기자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]를 완강했다! 🥳🥳🥳🥳뭔가 강의 제목을 딱 봤을때 진짜 엄청난 기초의 강의겠구나 했는데 강의를 들으면 들을수록 어느정도 깊이가 있어 조금 놀랐다. 오히려 기초가 없는 사람은 약간 따라하기 버거울 수도 있다는 생각이 들었다.기존 코드를 객체지향적으로 리팩토리하는 부분에서 많은 인사이트를 얻어서 뭔가 최태현님의 다른 강의들까지 들으면 남들과 조금은 다른 개발자가 될 수 있지 않을까란 생각도 들었다. 아무래도 서적에서 공부하는 것과 실무가 약간 버무러진 코드와 깊이 있는 코드를 경험하는 것은 너무 다르니까..! 아무튼 너무 기초적이지 않는 강의에 만족도가 높았다.직장인이라 잦은 야근과 피로감에 미니 프로젝트의 진도가 더디게 나가서 매우 아쉬웠다. 인프런 워밍업 클럽이 끝나도 미니 프로젝트는 개발을 계속 시도해봐야겠다.정말 유익하고 열정적인 3주였다. 나 포함 참여한 인프러너와 코치님까지!모두 3주 동안 너무 고생하셨습니다!오프라인 수료식에 참여하고 싶었지만 평일인데에다 판교라 참여하지 못하는 것이 너무 아쉽네요.온라인으로라도 참여하겠습니다열정적인 개발자분들 앞으로도 화이팅하세요🥳그리고 하루에 과제가 하나씩 있었을 때는 정말 미리 해놓지 않으면 직장인은 따박따박 당일에 수료하기 어려운 코스인 거 같아 이런 부분만 조금 개선되었으면 좋을 거 같습니다! ex) 과제는 미리 내놓되 완료 체크는 주말에 하기 같은..참고로 미니 프로젝트는 현재진행중입니다! 평일에는 시간이 없어 주말에 후다닥 개발을 하려고 하는 중입니다.https://devhan.tistory.com/327

백엔드인프런워밍업클럽

konakyeon3

[인프런 워밍업 스터디 클럽] 0기 - 마지막 발자국 미니프로젝트 완료~배포

미니 프로젝트 1단계 완료미니 프로젝트를 하면서 크게 깨닫은 점은 공부하게 된 것은 직접 프로젝트를 해봐야 강의나 책으로 배운 지식이 이해가 된다는 점이다.강의 자료를 따라치는 것이 아니라 직접 뭔가를 만들다 보면 문제를 겪고 해결하면서 뭔가를 배우게 되는 것 같다.- 동일 데이터 중복 입력 막기 2024-02-27 19:36:43.073 ERROR 28072 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result: 3; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 3] with root cause 완전히 동일한 팀 이름을 가진 팀을 2개 이상 등록하고, findByName을 통해 DB에서 해당 이름을 가진 팀을 조회해서 가져오는 중에 이런 에러가 발생했다.구글링해보니 단일 결과가 예상되는 JPA 리포지토리의 메서드의 리턴 값이 복수로 나왔을 때 이런 에러가 발생한다고 한다.처음부터 동일한 데이터가 중복해서 등록되는 것을 막았다면 이런 에러를 피할 수 있다.나는 해당 해당 팀명에 unique key를 걸어서 처음부터 동일한 팀이름이 등록되지 않도록 막았다. create table team ( id bigint auto_increment, name varchar(255) not null, primary key (id), unique key (name) ); 최태현 멘토님이 코드리뷰 시간에 이와 비슷한 내용에 대해 이야기했었다.만약에 어떤 사람이 회원 가입 버튼을 매운 짧은 시간에 여러번 눌러서 동일한 데이터가 중복해서 들어가게 되는 경우가 있다고 한다.이런 일이 벌어지는 원인은 스프링은 보통 요청에 대해 1개의 스레드가 할당 된다. 한 스레드가 요청을 처리 하고 있는 사이에 다른 스레드가 다른 요청을 처리하게 된다.거의 동시에 같은 요청이 2번 들어 왔을 때 한 스레드가 등록을 진행하고 있는 사이에 다른 스레드가 요청을 처리하는 바람에 이런 일이 발생하는 것이다.이를 해결 하기 위해서 중복이 되면 안되는 데이터에 unique key를 걸어 놓거나 낙관적 락/비관적 락/ 유저 락 을 이용한다고 한다.[\[Spring\] 스프링 동시성 처리 방법(feat. 비관적 락, 낙관적 락, 네임드 락) — 성장하는 성하 Blog](https://ksh-coding.tistory.com/125)- 셀프 피드백 🤔 다른 방법으로 중복 데이터를 등록을 막을 수 있는 방법이 없을까?🤔실제 서비스를 운영하다 보면 중복 데이터 등록 말고 또 어떤 문제를 겪을 수 있을까?🤔 어떻게 하면 해당 문제를 미연에 방지 할 수 있을까?사실 서비스 운영 중에 생길 수 있는 문제는 검색해보면 된다![Challenges of using REST APIs ](https://appmaster.io/blog/challenges-of-using-rest-apis)Feedback: 실전에 가까운 프로젝트를 해보는 것이 좋을 것 같다.지금도 보면 유저 로그인을 스크립트 코드나 매크로를 이용해서 악의적으로 수백번 등록할 수 있는데 이를 어떻게 막아봐야 될 것 같다. 배포: Amazon linux2와의 싸움처음으로 AWS EC2를 이용해서 배포를 했다.글쓴이는 이전에도 라즈베리파이에 flask 서버를 올려서 배포를 해본적이 있었다.아래는 mysql을 설치 중에 나온 에러 메시지다.에러 메시지는 찍어 놓은게 없어서 일단 비슷한 에러를 겪은 사람의 에러 메시지를 가져왔다.[error "GPG key a is already installed" It happens](https://stackoverflow.com/questions/77884025/im-using-amazon-linux-and-trying-to-install-mysql-in-ec2-but-i-keep-getting-th)ient-8.0.36-1.el9.x86_64.rpm: Already downloaded [SKIPPED] mysql-community-client-plugins-8.0.36-1.el9.x86_64.rpm: Already downloaded [SKIPPED] mysql-community-common-8.0.36-1.el9.x86_64.rpm: Already downloaded [SKIPPED] mysql-community-icu-data-files-8.0.36-1.el9.x86_64.rpm: Already downloaded [SKIPPED] mysql-community-libs-8.0.36-1.el9.x86_64.rpm: Already downloaded [SKIPPED] mysql-community-server-8.0.36-1.el9.x86_64.rpm: Already downloaded MySQL 8.0 Community Server 3.0 MB/s | 3.1 kB 00:00 GPG key at file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022 (0x3A79BD29) is already installed The GPG keys listed for the "MySQL 8.0 Community Server" repository are already installed but they are not correct for this package. Check that the correct key URLs are configured for this repository.. Failing package is: mysql-community-client-8.0.36-1.el9.x86_64 GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022 Public key for mysql-community-client-plugins-8.0.36-1.el9.x86_64.rpm is not installed. Failing package is: mysql-community-client-plugins-8.0.36-1.el9.x86_64 GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022 Public key for mysql-community-common-8.0.36-1.el9.x86_64.rpm is not installed. Failing package is: mysql-community-common-8.0.36-1.el9.x86_64 GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql-2022사실 위 문제는 gpg 키를 갱신해주기만 하면 해결 된다.리눅스에서 뭔가를 설치하기 위해서는 rpm, yum, dnf 가 해당 서버의 리포지토리를 이용해서 설치파일을 자기 서버로 가져와서 설치를 진행한다.리포지토리에는 해당 설치 파일들이 저장되어 있는 외부 저장소의 주소가 적혀 있다.이 저장소 주소에 접속하기 위한 키가 gpg이다. 기존의 gpg키로는 접속할 수 없었기에 때문에 gpg키를 2023 버전으로 갱신하니까 문제가 해결 되었다. 셀프 피드백🤔 AWS EC2 서버는 진짜 안전한 서버인가?일단은 내 ip만 해당 웹사이트에 접속하도록 바꿔 놓았고, 다른 보안 문제는 없는지 확인하고 다시 열 예정이다. AWS EC2에 대해 아는 것이 없으니 공부가 필요한 것 같다. 🤔 왜 많은 사람들이 다른 수많은 클라우드 서비스 중에서 AWS를 이용해서 배포를 할까?🤔AWS EC2가 가지고 있는 장점은 무엇일까?눈에 보이는 장점은 12개월 무료라는 점과 돈만 더 내면 쉽게 확장 할 수 있다는 점인 것 같다.라즈베리파이를 이용해서 배포를 할 때는 서버 로그 기록에 가끔 해외에서 ssh 로그인을 하려는 시도가 있다고 나올 때가 있었다.해외 접속을 아예 차단 해놔야 마음이 편하다. 아니면 다른 보안이 필요할 것 같다. 🧐 혼자서 문제를 해결하는 것도 정말 좋지만, 질문을 통해서 문제를 해결하는 것도 나쁜 것이 아니다.계획 상으로는 1,2시간 안에 끝날 줄 알았는데 거의 하루 종일 거의 10시간 이상 써서 배포 하는데 시간을 지체한 것 같다.이럴 때는 멘토님이나 아는 개발자분에게 질문을 해서 빠르게 문제를 해결하는 것도 나쁜 방법은 아닌 것 같다.느낀점생각보다 꾸준히 코드를 매일 공부하는 것이 쉽지 않았다. 어려운 개념을 이해한다거나 뭔가 어려운 것을 구현하는 것이 아니라 시간을 들이면 어떻게든 해결 할 수 있는 과제라서 쉽게 끝났던 것 같다.문제는 건강이다. 하루에 10시간 이상 앉아 있으니 바로 목이 너무 아프다.운동을 좀 하자.이번 스터디는 스터디원들이 적극적으로 스터디에 참여하고 열심히 공부를 하는 게 눈에 보여서 자극을 많이 받았다. 다들 열심히 사니까 나도 열심히 살아야지 이번 스터디를 이끌어주신 최태현 멘토님과 인프런에게 정말 감사합니다. 참고- 아래 강의 링크의 내용을 바탕으로 글이 작성됨[자바-스프링부트-서버개발-올인원-인프런](https://www.inflearn.com/course/lecture?courseSlug=%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EC%98%AC%EC%9D%B8%EC%9B%90&unitId=208208)- SpringBoot 공식 문서[Spring Boot Reference Documentation](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#getting-started) 

빠타박스

[Tip] _ 우리가 처음 UE5 을 시작할 때 잘 모를 만한 상황이나 해결법

우리가 Unreal Project 생성시 알아야 하는 부분이 있습니다.프로젝트 생성절대 경로상에 한글, 띄어쓰기, 숫자가 있는 것이 있으면 안됩니다.즉. 01_Unreal Engine X언리얼엔진01 X언리얼ENgine X폴더 명 For Project Unreal X위처럼 작성시 언리얼이 해당 경로를 찾을 수 없습니다. 간단히 생각해보면 쉬운데, 우리가 언리얼 에서 헤더 부분을 잘 보시면 됩니다. 헤더부분에 띄어쓰기가 되어있거나. 한글로 적힌 부분이 없습니다. 애초에 한글이나. 띄어쓰기가 있을 경우나 숫자가 먼저 나오는 폴더경로에 있을경우 언리얼 프로젝트가 해당 파일을 찾을 수 없게 됩니다.   혹시나 build 에러가 뜨시거나 한다면 먼저 경로 설정을 확인해 보세요!Live CodingUnreal Engine 5 부터는 Live Coding이라는게 들어가 있습니다. 이전 4에서는 C++에서 빌드후 돌아왔을때 에디터 창이 크래시 나게 되면 피치 못하게 저장이 안되서 날라가는 경우도 있었고, 크래쉬가 나게되어 문제의 원인을 찾지 못하면 프로젝트를 못열고 문제가 되는 부분을 찾아서 코드 수정 후 빌드해서 다시 열거나 프로젝트를 이전 백업파일을 가져와서 열어야 하던 시절이 있었습니다. 라이브코딩은 해당 문제에 대한 부분을 찾아주고 에디터로 오는 특정 문제 되는 부분을 조금 예방할 수 있다는 게 있습니다.하단에 이렇게 눌러서 하거나.하단 창 우측에 이렇게 라이브 코딩을 바로 실행할 수 있게되어 있습니다. 이전 처럼 작업이 라이브코딩 없이 Visual studio상 빌드로만 하시는게 편하시거나. 디버깅 할 때 라이브코딩 충돌을 일으키므로 이렇게 점 있는 부분에 속성 부분에서 Enable Live Coding을 꺼서 사용도 가능합니다.이외로 많이 겪는 문제는 많습니다.Unreal C++ Blueprint 주소 경로 가져올 때 '_C ' 를 명시하는 것이나. 이것은 해당 < > 템플릿 안에 무엇이 들어가 있는가에 따라 불러오는 것이 위젯인지 pawn 인지 Actor인지 등등 이 후 추가 사항이 있다면 올리겠습니다~ 

게임 프로그래밍UnrealEngineGameGameDevelopppatabox신충식unrealbugs

이양구

[인프런 워밍업 클럽 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미션과제발자국

konakyeon3

인프런워밍업스터디_BE_0기 2주차 발자국

2주차 학습 내용 요약 6일차 스프링컨테이너의 의미와 사용방법6일차 스프링 컨테이너의 개념 미션 수행강의에서는 스프링 빈과 컨테이너 개념에 대해 알아보았다.@Component @Bean 어노테이션을 넣으면 해당 클래스가 런타임 시에 스프링컨테이너에 등록을 하게 된다는 점을 배웠다.인스턴스화를 자동화 해준다는 느낌이다.미션으로는 기존의 코드를 Controller - Service - Repository 로 코드를 분리시켜서 리펙토링하는 것이 미션이였다.리펙토링하는 코드를 작성 할 때 Controller 클래스가 기존에 작성한 코드에 종속되어 있어서 api를 호출 할 때마다 오류가 났었다.그래서 아예 새로운 Controller 클래스를 만들었다.7일차 JPA를 왜 사용하는가_어떻게 JPA 사용하는가7일차 JPA를 왜 사용하는가_어떻게 JPA 사용하는가JPA에 대해 학습하고, 기존 코드를 JPA로 리펙토링하는 것을 미션으로 수행했다.아직은 왜 정확히 sql 쿼리문에 JPA를 사용하는지 납득이 되지 않는다.JPA를 사용할 때 제일 큰 장점은 유지보수성을 높여준다는 것이라고 한다.객체지향 구조 사용해 이미 만들어진 객체를 활용할 수 있다. sql쿼리문으로 db 테이블마다 crud sql문들을 다 작성해야 하지만 jpa 코드는 기존에 만들어진 메서드와 엔티티 객체를 사용해서 중복을 줄이고 유지보수 하기도 편해진다고 한다. 🤔 몇 만줄 되는 복잡한 코드를 유지보수 해본 경험이 없으니 JPA의 이점에 대해 납득하지 못하는 것 같다.직접 프로젝트를 진행하면서 JPA를 사용하는 것과 사용 안하는 프로젝트를 비교를 해볼 수 밖에 없겠다. 그리고 직접 코드를 작성 했을 때는 JPA 코드가 sql 쿼리문에 비해 직관적이지 않았다.JPA repository 인터페이스의 메서드들을 직접 코드를 사용하기 전까지는 무슨 뜻인지 알 수 없었다. 🤔이 부분은 아직 JPA코드를 많이 작성 해보지 않아서 생기는 문제인 것 같다.최대한 다양한 JPA 코드를 작성해보자. 8일차 트랜잭션이 무엇이고_왜 사용해야 하는가🔥 데이터베이스는 원자성 속성을 보장하지 않습니다. 여러 데이터베이스 작업이 "전부 아니면 전무" 기준에 따라 하나의 원자 단위로 실행된다는 보장은 없습니다. 일련의 작업에서 한 번 작업이 실패하면 데이터베이스는 계속해서 다음 작업을 실행합니다https://www.java4coding.com/contents/oracle/oracle-transaction)트랜잭션을 사용해야 데이터베이스의 원자성을 회복시킬 수 있다. 예를 들자면 이런 식이다.유저를 등록하고 해당 유저에 대한 반납 기록 저장하는 sql로직을 둘을 동시에 해야 되는데, 트랜잭션을 사용하지 않으면 유저는 등록했는데 반납 기록이 저장되지 않는다거나 반납 기록만 저장되서 DB 저장에 문제가 생길 수 있다.@Transaction을 사용한 코드는 한쪽 sql 로직이 이루어지지 않으면 다른 로직도 롤백시키는 일을 해준다. 🤔 생각해보면 트랜잭션을 사용하지 않으면 있어야 될 내용이 DB에 없거나, 없어야 될 내용이 있는 등 끔찍한 일이 벌어질 게 뻔하다.정말정말 트랜잭션을 중요한 내용인 것 같다. 9일차 조금 더 복잡한 기능을 API로 구성하기libraryapp에 반납기능과 책 대출 기능을 만드는 것을 실습했었다.show tables 는 sql문은 해당 데이터베이스 안에 어떤 테이블이 있는지 알려준다. 🤔 show tables 를 정말 자주 사용했다.테이블 삭제, 생성, 구조 변경 코드도 많이 사용했던 것 같다. 왜 일까?쿼리문 여러개를 동시에 실행하다보니 비슷한 이름의 테이블이 중복되서 만들어지는 일이 많았다. 그래서 데이터베이스에 어떤 테이블이 있는지 확인하는 코드가 필요했다. 그래서 use tables또 다른 DB 관련 문제로는 같은 이름을 지닌 책이 중복해서 등록이 되거나 같은 이름을 가진 유저가 중복되서 등록되는 일이 벌어지기도 했었다. 이 문제는 나중에 이름 부분을 unique key로 등록해서 중복 등록을 방지하는 것으로 해결했다. 간단한 ERD를 먼저 그려보고 코딩을 하거나, DB 설계 내용을 그림에 먼저 그려 보고 나서 코딩을 했다면 이렇게 복잡해지지 않았을 것 같다. 정리뭔가 미션을 수행하면 시간이 없어서 허겁지겁 끝내는 느낌이다.시간이 없다면 최대한 미션에서 명세한 기능만이라도 만들어서 코드를 제출하던가 해야되는데 뭔가 쓸떼 없는 일에 집착하다가 늦어지는 것 같다.무작정 코드를 짜게 되면서 문제가 생기는 경우도 많은 것 같다.erd를 그려본다거나 간단한 코드라도 종이에 먼저 어떻게 코드를 작성할지 써보고 코드를 작성하는 습관을 만드는 것이 좋을 것 같다.이런 나의 생각과 느낌을 최대한 넣어서 작성하는 회고도 좋지만, 정량적인 지표들(코드 작성 시간, 미션 수행에 들어간 시간 측정, 미션 수행 난이도, 등)을 이번 주에 대해 최대한 객관적으로 평가해볼 필요가 있을 것 같다.내가 아닌 제3자의 피드백도 필요하다. 

이동원

[인프런 워밍업 스터디 클럽 0기_BE] 2주차 회고록 정리

짧은 다리의 두 번째 발걸음 6일차 역할의 분리와 스프링 컨테이너 Spring Bean: 스프링 컨테이너 안으로 들어간 클래스를 스프링 빈이라 함@Service와 @Repository를 통해서 Service와 Repository도 스프링 빈으로 등록가능함 @Primary: 어노테이션의 일좀으로 우선권을 정해줄 수 있다 @Configuration: @Bean과 함께 사용 @Bean: 메소드 객체를 스프링에 등록할 때 사용 @Component: @Controller, @Service, @Repository 모두 아니면서, 직접 작성한 클래스를 스프링 빈으로 등록한다 7일차 Spring Data JPA를 사용한 DB 조작 SQL 문 직접 작성의 문제-> 문자열 작성이어서 오타 발생시 알아채기 어려움, 컴파일 시점에 발견안되고 런타임 시에 발견된다-> 특정한 DB에 종속된다-> 테이블 마다 CRUD반복 작업이 필요하다-> 객체와 달리, 양방향이 아닌 단방향이다 JPA 를 통해 문제해결: 객체와 관계형 DB 테이블을 짝지어 데이터를 영구적 저장하도록 해주는 JAVA 진영 규칙 spring: datasource: url: "jdbc:mysql://localhost/library" username: root password: driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql: true dialect: org.hibernate.dialect.MySQL8Dialect 8일차 트랜잭션과 영속성 컨텍스트를 이용한 요구사항 구현 트랜잭션: DB의 논리적 연산 단위. 1개의 트랜잭션에는 N개(≥1)의 SQL문 포함 @Transcational붙어있는 메서드가 시작할 때 트랜잭션을 시작예외 없이 잘 끝났다면 commit오류라면 rollback 영속성 컨텍스트: 테이블과 매핑된 Entity 객체를 관리/보관하는 역할, 스프링에서는 트랜잭션 사용 시 영속성 컨텍스트가 생성, 트랜잭션 종료 시 영속성 컨텍스트가 종료 특징변경감지쓰기 지연1차 캐싱회고: 과제가 끝나면서 조금 마음이 가벼워진 느낌이 있었다. 실제 프로젝트를 하려하니 생각이 많아지고 어떻게 하면 좋을지 고민하는 시간이 많았다. 코치님의 각 주차에 따라 내용을 따라는 가는것 같은데 막상 내것이 되었나 생각하며 코드를 치려고 하니 시간이 배로 걸리고 오류가 자주났다. 진짜 하,,,,,,,, 라고 한숨만 하루에 100번도 넘게 쉰적이 많았다. 특히나 db연결하는데 있어서 인텔리제이랑 mysql연동해놓고 하다가 컴퓨터 껐다가 켜서 시작 프로그램이 좀 지저분해 몇개 지우니 db연결이 끊기고 다시 연결안되는 문제로 한나절 고생한것을 생각하니 진짜 암담했다. jpa에 대해서 말만 듣고 실제 해보니 마법과도 같은 편리함과 놀라움이어서 재밌었다. 문법을 좀 알고 공부해야 할것 같은데. 이렇게 나마 써보고 배울수 있어서 유익한 시간이었다. 각 주차에 따라, 따라가는 것이 좀 버겁지만 걸어가든 기어가든 완주를 하면서 얻는것이 많다고 생각하기에 끝날때까지 복습하면서 완주해낼 생각이다. 남은 시간도 스스로에게 부끄럽지 않게 공부할수 있길 바란다. 

망고123

인프런 워밍업 클럽 스터디(BE) 0기 / 2주차 발자국

일주일 간의 학습 내용에 대한 간단한 회고📕지난 1주차 회고에서는 10000자 되는 내용으로 깊게 정리했습니다.😅 2주차는 1주차처럼 모든 내용을 담은 정리하지 않고, '핵심 포인트' 위주로 필요하다고 생각되는 부분의 요약과 함께 내용을 정리하는 2주차 회고를 작성합니다😀 (10000자 -> 4000자) 🔥무엇보다 학습 내용 범위 벗어난 스펙과 기술을 사용하지 않는 것을 원칙으로 학습하고 있습니다. 처음 시작할 때의 열정과 목표를 잊지 않으면서, 강의를 통해 멘토님께서 전달하고자 하는 지혜와 경험을 쌓아가며 <미니 프로젝트> 의 모든 단계를 성공적으로 마무리할 수 있도록 성장하기를 나, 스스로에게 응원합니다. 일주일 동안 스스로 칭찬하고 싶은 점 하루도 빠지지 않고 열심히 학습하고 달려왔고, 어떻게 해야 좀 더 효과적인 학습을 할 수 있을까에 대해 고민하고 행동으로 실천하여 효율적인 학습을 할 수 있었습니다.아쉬웠던 점 감기에 걸려서 제대로 학습하지 못한 날이 꽤 있었습니다. 아파서 학습하지 못한 이 공백을 채우기 위해 더욱 집중력 있게 학습할 수 있도록 노력해야 겠다고 생각했습니다.보완하고 싶은 점 2 주차는 깊게 정리하지 않고 유연하게 정리하면서 멘토님의 PPT와 PDF 를 중심으로 학습하면서 훨씬 효율적인 복습을 했습니다. 복습하면서 아직은 완벽하게 체득하지 못한 부분들이 꽤 있다는 것을 <미니 프로젝트>를 진행하면서 느끼게 됐습니다.다음주에는 어떤 식으로 학습하겠다는 스스로의 목표 개인적으로 진행하고 있는 프로젝트와 학습하는 것의 비중을 낮추고, <미니 프로젝트> 를 중심으로 부족한 부분들에 대한 이론과 예제를 개인적으로 정리하면서 진행하려고 합니다. 기회가 된다면 <미니 프로젝트> HTML이라도 구현하는 것을 목표로 하고 있습니다.   학습 내용 정리19강. UserController와 스프링 컨테이너@RestController는 컨트롤러 클래스를 API 진입 지점으로 만들어주고 스프링 빈으로 등록시킵니다. 스프링 빈은 스프링 컨테이너에 들어간 클래스를 의미합니다. 스프링 빈에 등록된 클래스들을 식별하기 위해 이름 및 타입과 함께 다양한 정보가 저장되며 인스턴스화를 수행합니다. 그리고 JdbcTemplate 역시 스프링 빈으로 등록되어 있습니다. build.gradle 안의 spring-boot-starter-data-jpa 의존성에 의해JdbcTemplate 을 스프링 빈으로 미리 등록됩니다.따라서 스프링 컨테이너는 UserController 를 인스턴스화할 때, UserController에서 필요한 JdbcTemplate을 스프링 컨테이너 내부에서 찾아 인스턴스화를 진행하게 됩니다. 따라서 JdbcTemplate을 스프링 빈으로 등록하는 의존성인 spring-boot-starter-data-jpa 이 없으면 에러가 발생한다는 점을 참고합니다.스프링 부트 서버를 실행하면 다음과 같은 일이 순차적으로 내부에서 실행됩니다.스프링 컨테이너가 시작합니다.스프링 컨테이너에 기본적으로 많은 스프링 빈이 등록됩니다.( JdbcTemplate이 등록됩니다.)개발자가 작성한 스프링 빈이 등록됩니다.( UserController 가 등록됩니다.)필요한 의존성이 자동으로 설정됩니다.( UserController 를 만들 때 JdbcTemplate 을 알아서 넣어줍니다.)이제 지금까지 UserRepository 가 JdbcTemplate 을 바로 가져오지 못하는 이유를 알 수 있습니다. UserController 는 @RestController 에 의해 스프링 빈에 등록하고 동일한 스프링 빈인 JdbcTemplate 을 가져올 수 있지만, UserRepository 는 스프링 빈이 아니기 때문에 가져올 수 없습니다. 따라서 서비스와 리포지토리를 스프링 컨테이너에 등록하기 위해 @Service, @Repository어노테이션을 사용해야 합니다.정리하자면 UserController - UserService - UserRepository 클래스는 서버가 시작할 때 다음과 같이 수행됩니다.JdbcTemplate 을 이용해 UserRepository 가 스프링 빈으로 등록됩니다. (인스턴스화를 수행합니다.)UserRepository 를 의존하는 UserService 가 스프링 빈으로 등록됩니다.UserService 를 의존하는 UserController 가 스프링 빈으로 등록됩니다.이렇게 3개의 클래스 모두 스프링 빈으로 등록됩니다!20강. 스프링 컨테이너를 왜 사용할까?!MySQL 을 사용하여 데이터를 저장하는 방식으로 변경하면 다음과 같은 일이 발생됩니다.BookMemoryRepository 대신하는 BookMySqlRepository 를 생성합니다. JdbcTemplate 을 생성자로 받을 수도 있지만, BookMySqlRepository 가 직접 설정해 준다고 가정합니다.BookService 도 변경됩니다. BookMemoryRepository()대신 BookMySqlRepository() 를 사용합니다.서비스까지 변경되는 것이 바로 가장 큰 문제입니다. 데이터를 메모리에 저장할지 MySQL에 저장할지에 대해서만 변경하고 싶지만, BookService까지 필연적으로 변경이 일어나게 됩니다. 이 고민에 대한 해결책이 바로 스프링 컨테이너입니다.스프링 컨테이너를 사용한다고 가정합니다.스프링 컨테이너는 BookMemoryRepository 혹은 BookMySqlRepository 중 하나를 선택한 후, BookService 를 생성합니다. 이런 방식을 어려운 말로 제어의 역전 (IoC, Inversion of Control)이라 부릅니다.또한 컨테이너가 BookService 를 생성할 때 BookMemoryRepository 와 BookMySqlRepository 중 하나를 선택해서 넣어주는 과정을 의존성 주입(Dependency Injection)이라고 합니다.@Service public class BookService { private final BookRepository bookRepository; public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } }public interface BookRepository { public void save(String bookName); }@Repository public class BookMemoryRepository implements BookRepository { @Override public void save(String bookName) { println("Memory Repository " + bookName); } }@Repository @Primary // 우선권을 부여하는 어노테이션!! public class BookMySqlRepository implements BookRepository { @Override public void save(String bookName) { println("MySQL Repository " + bookName); } }스프링 컨테이너에 BookMemoryRepository 혹은 BookMySqlRepository 둘 중 어느 것을 등록할 지에 대해서는@Primary 어노테이션을 이용해 우선권을 제어할 수 있습니다.21강. 스프링 컨테이너를 다루는 방법서비스와 리포지토리 클래스를 @Service, @Repository 어노테이션으로 스프링 빈으로 등록했습니다. 이 방식 뿐만 아니라 다른 어노테이션으로도 스프링 빈에 등록할 수 있습니다.@Configuration : 클래스에 붙이는 어노테이션. @Bean을 사용할 때 함께 사용해 주어야 합니다.@Bean : 메서드에 붙이는 어노테이션. 메서드에서 반환되는 객체를 스프링 빈에 등록합니다.다음 예제는 UserRepository 를 @Configuration 과 @Bean 을 활용한 예제입니다.@Configuration public class UserConfiguration { @Bean public UserRepository userRepository(JdbcTemplate jdbcTemplate) { return new UserRepository(jdbcTemplate); }        @Bean public UserService userService(UserRepository userRepository) { return new UserService(userRepository); } }그렇다면 언제 @Service, @Repository 를 사용해야 하고, 언제 @Configuration + @Bean 을 사용해야 할까요? 정답은 없습니다!일반적으로 개발자가 직접 만든 클래스를 스프링 빈으로 등록할 때에는 @Service, @Repository 를 사용합니다.외부 라이브러리, 프레임워크의 생성된 클래스를 스프링 빈으로 등록할 때 @Configuration + @Bean 조합을 많이 사용하게 됩니다.@Component 어노테이션은 @RestController, @Service, @Repository, @Configuration 모두 가지고 있습니다. @Component 어노테이션을 붙이면 주어진 클래스를 ‘컴포넌트'로 간주하고, 컴포넌트들은 스프링 스프링 서버가 뜰 때 자동으로 감지됩니다. @Component 덕분에 지금까지 우리가 사용했던 어노테이션들이 모두 자동으로 감지된 것입니다.@Component어노테이션은 컨트롤러, 서비스, 리포지토리가 아니라 추가적인 클래스를 스프링 빈으로 등록할 때 종종 사용됩니다.스프링 빈으로 등록하는 방법을 살펴보았으니, 스프링 빈을 주입받는 방법은 다음과 같습니다. 가장 간단하고 권장되는 방법은 생성자를 이용해 주입받는 방법입니다. 지금까지 우리가 계속 사용한 방법입니다.@Repository public class UserRepository { private final JdbcTemplate jdbcTemplate;     // 생성자에 JdbcTemplate이 있으므로 스프링 컨테이너가 넣어준다. public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }두 번째 방법은 setter 주입 방식입니다. final 키워드를 제거하고 setter 메서드에 @Autowired 어노테이션을 작성해야 합니다.@Repository public class UserRepository { private JdbcTemplate jdbcTemplate; // 1. final 제거        @Autowired // 2. @Autowired 추가 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }@Autowired 어노테이션이 있어야 스프링 컨테이너에 있는 스프링 빈을 찾아 setter 메서드에 넣어 주게 됩니다.세 번째 방법은 필드에 직접적으로 주입하는 방법입니다. 필드 위에 @Autowired 어노테이션을 작성합니다.@Repository public class UserRepository { @Autowired // 필드에 @Autowired 추가 private JdbcTemplate jdbcTemplate; }setter 주입 방식과 필드에 바로 주입하는 방법은 기본적으로 권장되지 않습니다.setter를 사용하게 되면 혹시 누군가가 setter를 사용해 다른 인스턴스로 교체해 동작에 문제 가 생길 수도 있고,필드에 바로 주입하게 되면 테스트가 어렵기 때문입니다.@Qualifier어노테이션은 @Primary 어노테이션이 없는 경우에 주입받는 쪽에서 특정 스프링 빈을 선택할 수 있습니다.public interface FruitService {} // 과일 인터페이스@Service public class AppleService {} // 사과 클래스@Service public class BananaService {} // 바나나 클래스@Service public class OrangeService {} // 오렌지 클래스@RestController public class UserController { private final UserService userService; private final FruitService fruitService;     public UserController( UserService userService, @Qualifier("appleService") FruitService fruitService) { this.userService = userService; this.fruitService = fruitService;       } }@Qualifier("appleService") FruitService fruitService)에 의해 FruitService 에는 AppleService 가 들어오게 됩니다.@Qualifier 어노테이션은 스프링 빈을 사용하는 쪽과 스프링 빈을 등록하는 쪽 모두 사용할 수 있습니다. 이 경우에는 @Qualifier 어노테이션에 적어준 값이 같은 것끼리 연결됩니다.@Service @Qualifier("main") public class BananaService {}@RestController public class UserController { private final UserService userService; private final FruitService fruitService; public UserController( UserService userService, @Qualifier("main") FruitService fruitService) { this.userService = userService; this.fruitService = fruitService; } }만약 @Primary 와 @Qualifier 를 둘 다 사용하고 있으면 @Qualifier 의 우선 순위가 높습니다. 왜냐하면 스프링 빈을 사용하는 쪽에서 특정 빈을 지정해 준 것이 더욱 우선 순위를 높게 간주합니다.22강. Section3 정리좋은 코드가 왜 중요한지 이해하고, 원래 있던 Controller 코드를 보다 좋은 코드로 리팩 토링한다.스프링 컨테이너와 스프링 빈이 무엇인지 이해한다.스프링 컨테이너가 왜 필요한지, 좋은 코드와 어떻게 연관이 있는지 이해한다.스프링 빈을 다루는 여러 방법을 이해한다.23강. 문자열 SQL을 직접 사용하는 것이 너무 어렵다!!SQL을 직접 작성해 개발하게 되면서 '컴파일 타임 에러 체크 불가능', '특정 데이터베이스에 종속', '수많은 반복 작업', '데이터베이스 테이블과 객체의 패러다임' 등 이러한 어려움이 있었습니다. 그래서 사람들은 JPA를 만들게 되었습니다. JPA란 Java Persistence API의 약자로 자바 진영의 ORM(Object-Relational Mapping) 기술 표준을 의미합니다.영속성(Persistence)은 데이터를 생성한 프로그램이 종료되더라도, 그 데이터는 영구적인 속성을 갖는 것을 의미합니다.API는 우리가 만든 HTTP API에서도 쓰였지만, ‘정해진 규칙’을 의미합니다.그럼 여기까지 정리해 보면, JPA는 데이터를 영구적으로 보관하기 위해 Java 진영에서 정해진 규칙입니다. 이제 ORM(Object-Relational Mapping)에 대해 이해합니다.Object 단어는 우리가 Java에서 사용하는 ‘객체’와 동일합니다.Relational 의미는 관계형 데이터베이스의 ‘테이블’을 의미합니다.Mapping이라는 의미는 말 그대로 둘을 짝지어 준다는 의미입니다.여기까지 정리하면 JPA란 다음과 같이 이해할 수 있습니다. 🔥 객체와 관계형 데이터베이스의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙JPA(ORM)는 규칙(Interface)이기 때문에 구현체가 필요합니다. 따라서 JPA 를 실제 코드로 작성한 가장 유명한 프레임워크가 바로 Hibernate 가 있습니다. Hibernate은 내부적 으로 JDBC를 사용하고 있습니다. 그림으로 나타내면 다음과 같습니다.24강. 유저 테이블에 대응되는 Entity Class 만들기User 객체에 @Entity 어노테이션을 작성합니다. Entity는 ‘저장되고, 관리되어야 하는 데이터’를 의미합니다. 어노테이션은 마법 같은 일을 해준다고 했습니다. @Entity 를 붙이게 되면, 스프링이 @Entity 인식하여 서버가 동작하면 User 객체와 user 테이블을 같은 것으로 간주합니다.user 테이블에만 존재하는 id를 User 객체에 추가합니다.id는 테이블에서 primary key 를 의미합니다.@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null;     // 생략... }@Id : 이 필드를 primary key로 간주한다.@GeneratedValue : primary key는 DB에서 자동 생성해 주기 때문에 이 어노테이션을 붙여주어야 합니다. DB의 종류마다 자동 생성 전략이 다른데, MySQL의 경우 auto_increment 를 사용합니다. 이 전략은 IDENTITY 전략과 매칭됩니다.JPA에 의해 테이블과 매핑된 객체는 파라미터를 가지지 않은 기본 생성자가 꼭 필요합니다. 현재는 User(String name, Integer age) 파라미터를 2개 가진 생성자만 있기 때문에 에러가 발생합니다. 기본 생성자도 추가할 때 protected 해도 괜찮습니다.@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null;        @Column(nullable = false, length = 20, name = "name") private String name;     private Integer age;     protected User() { } // 생략... }Column에 대해 @Column 어노테이션으로 다양한 옵션을 설정할 수 있습니다. 주로 필드에 null이 들어갈 수 있는지의 여부, 길이 제한, DB에서의 column 이름을 설정합니다. 지금은 User 객체와 user 테이블의 필드 이름이 같지만, 다를 경우 @Column 어노테이션을 통해 설정해 주면 됩니다.@Column 어노테이션이 존재하지 않는 필드이더라도 JPA는 해당 필드가 Table에도 있을 거라 생각합니다. 예를 들어 private Integer age 라는 필드는 자동으로 user 테이블의 age 와 매핑하게 됩니다.이제 최초 JPA를 적용할 때 설정해 주는 옵션을 추가합니다. application.yml 파일을 찾은 다음과 같이 입력합니다.spring: datasource: url: "jdbc:mysql://localhost/library" username: "root" password: "" driver-class-name: com.mysql.cj.jdbc.Driver ### 아래 부분이 추가되었다!!! ### jpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql: true dialect: org.hibernate.dialect.MySQL8Dialectspring.jpa.hibernate.ddl-auto스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지에 대한 옵션.create : 기존 테이블이 있다면 삭제 후 다시 생성.create-drop : 스프링이 종료될 때 테이블을 삭제.update : 객체와 테이블이 다른 부분만 변경.validate : 객체와 테이블이 동일한지 확인.none : 별다른 조치를 하지 않음.현재 우리는 DB에 테이블이 잘 만들어져 있고, 미리 넣어둔 데이터도 있으므로 none 이라 설정합니다.spring.jpa.properties.hibernate.show_sql : JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여줄지 결정. (true)spring.jpa.properties.hibernate.format_sql : JPA를 사용해 DB에 SQL을 날릴 때 SQL을 예쁘게 포맷팅할지 결정. (true)spring.jpa.properties.hibernate.dialect dialect : 한국어로 방언, 사투리라는 의미. 이 옵션을 통해 JPA가 알아서 Database끼리 다른 SQL을 조금씩 수정합니다. 우리는 MySQL 8버전을 사용하고 있으므로org.hibernate.dialect.MySQL8Dialect 로 설정하면 됩니다.엔티티 생성과 application.yml 설정으로 객체와 테이블 간의 매핑을 모두 마쳤습니다. 다음 시간에 SQL 을 직접 작성하지 않고 DB에 쿼리를 수행합니다.25강. Spring Data JPA를 이용해 자동으로 쿼리 날리기SQL을 작성하지 않고 유저 테이블에 쿼리를 수행합니다. 유저 생성 / 조회 / 업데이트 기능을 리팩토링합니다.User 도메인 객체와 같은 위치에 UserRepository 라는 인터페이스를 생성.public interface UserRepository {}JpaRepository 를 상속. ( 매핑 객체인 User 와 유저 테이블의 id인 Long 타입을 작성)public interface UserRepository extends JpaRepository<User, Long> {}UserService 에서 직접 SQL 작성을 작성한 UserRepository 대신 새로운 UserRepository 를 사용합니다. 가장 먼저 UserService 의 저장 기능부터 변경합니다.// JDBC 구현 public void saveUser(UserCreateRequest request) {    userJdbcRepository.saveUser(request.getName(), request.getAge()); } ​ // Spring Data JPA 구현 public void saveUser(UserCreateRequest request) { userRepository.save(new User(request.getName(), request.getAge())); } ​ // Spring Data JPA 구현 - id 출력하기 public void saveUser(UserCreateRequest request) { User user = userRepository.save(new User(request.getName(), request.getAge())); System.out.println(user.getId()); }조회 기능도 변경을 변경합니다.// JDBC 구현 public List<UserResponse> getUsers() { return userJdbcRepository.getUserResponses(); } // Spring Data JPA 구현 public List<UserResponse> getUsers() { return userRepository.findAll().stream() .map(user -> new UserResponse(user.getId(), user.getName(), user.getAge())) .collect(Collectors.toList()); }findAll 메서드는 모든 유저 데이터를 조회하는 SQL이 수행되며 그 결과는 List 로 반환됩니다. List 를 UserResponse 으로 전달합니다. 만약 UserResponse 에서 User 를 받는 생성자를 작성하면 코드를 더욱 깔끔하게 변경할 수 있습니다.public List<UserResponse> getUsers() { return userRepository.findAll().stream() .map(UserResponse::new) .collect(Collectors.toList()); }다음으로는 업데이트 기능을 변경합니다.업데이트에서는 2번의 쿼리를 사용합니다.id를 통해 User를 가져와 User가 있는지 없는지 확인하고,User가 있다면 update 쿼리를 날려 데이터를 수정.// JDBC 구현 public void updateUser(UserUpdateRequest request) { if (userJdbcRepository.isUserNotExist(request.getId())) { throw new IllegalArgumentException(); } userJdbcRepository.updateUserName(request.getName(), request.getId()); }// Spring Data JPA 구현 public void updateUser(UserUpdateRequest request) { User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); userRepository.save(user); }findById는 id에 해당하는 1개의 데이터를 가져올 수 있습니다. 이때 Java 라이브러리의 Optional이 반환되는데, orElseThrow 를 사용하면 User가 비어있는 경우 에러를 던집니다. 반환된 User 객체의 이름을 업데이트해주고, 위에서 사용했던 save 기능을 호출하면 됩니다.setter 대신 updateName 으로 명시적인 이름을 붙여준 이유는 다음 링크 영상에서 참고할 수 있습니다.https://www.youtube.com/watch?v=5P7OZceQ69Q지금까지 사용한 기능은 다음과 같습니다.save : 주어지는 객체를 저장하거나 업데이트.findAll : 주어지는 객체가 매핑된 테이블의 모든 데이터를 가져옴.findById : id를 기준으로 특정한 1개의 데이터를 가져옴.그런데 한 가지 궁금한 점이 있습니다. 어떻게 SQL을 작성하지 않아도 쿼리가 나갈 수 있을까요? 객체와 테이블을 자동으로 매핑해 준 JPA가 처리해 준 것일까요? 정답은, 비슷하지만 조금 다릅니다. JPA를 이용하는 Spring Data JPA 가 자동으로 처리해준 것입니다. 23강에서 확인했던 JPA, Hibernate, JDBC 관계에 Spring Data JPA를 추가해 보면 다음과 같습니다.사용한 save, findAll 같은 메소드는 SimpleJpaRepository 에서 찾아볼 수 있습니다. 스프링을 시작하면 여러가지 설정을 해준다고 했는데, 스프링은 JpaRepository 를 구현 받는 리포지토리에 대해 자동으로 SimpleJpaRepository 기능을 사용할 수 있도록 합니다. SimpleJpaRepository 코드를 열어보면, 조금 복잡한 코드들을 확인할 수 있는데, 이게 바로 JPA 코드입니다. Spring Data JPA 를 사용하는 덕분에 복잡한 JPA 코드를 직접 사용하는 게 아니라, 추상화된 기능으로써 사용할 수 있습니다.이를 그림으로 표현해 보면 다음과 같습니다.26강. Spring Data JPA를 이용해 다양한 쿼리 작성하기유저 삭제 기능을 구현해 보고, Spring Data JPA를 이용한 다양한 조회 쿼리 작성 방법을 학습합니다.// JDBC 구현 public void deleteUser(String name) { if (userJdbcRepository.isUserNotExist(name)) { throw new IllegalArgumentException(); } userJdbcRepository.deleteUserByName(name); } 이름을 통해 유저 여부를 확인하고 delete 쿼리를 수행합니다. UserRepository 인터페이스에서 다음과 같은 메소드 시그니처를 작성합니다.public interface UserRepository extends JpaRepository<User, Long> { User findByName(String name); }User : 이름을 기준으로 유저 데이터를 조회해 유저 객체를 반환(유저 정보가 없다면, null 반환)findByName함수 이름으로 알아서 SQL 조립find는 1개의 데이터를 가져옴.By 뒤에 붙는 필드 이름으로 SELECT 쿼리의 WHERE 문이 작성됨.예를 들어, findByName 은 select * from user where name = ? 과 동일.findByName(String name) 을 통해 이름을 기준으로 User 정보를 가져올 수 있습니다. UserRepository에서 기본으로 제공되는 delete 메소드를 사용합니다.public void deleteUser(String name) { User user = userRepository.findByName(name); if (user == null) { throw new IllegalArgumentException(); } userRepository.delete(user); }UserController에서 UserServiceV2으로 변경하고 테스트를 수행합니다. UserService 인터페이스를 생성하여 다형성을 이용할 수도 있지만, 간단한 작업이므로 객체 타입 전체를 변경합니다.@RestController public class UserController { // UserServiceV2를 사용하도록 변경 private final UserServiceV2 userServiceV2;     public UserController(UserServiceV2 userServiceV2) { this.userServiceV2 = userServiceV2; } }생성 / 조회 / 업데이트 / 삭제 기능까지 모두 JDBC 대신 Spring Data JPA를 사용해 잘 동작하는 것을 확인할 수 있습니다. Spring Data JPA의 추가적인 쿼리 작성법에 대해 학습합니다.By 앞에는 다음과 같은 구절이 들어갈 수 있습니다.find : 반환 타입은 객체가 될 수도 있고, Optional<타입> 이 될 수도 있음.findAll : 쿼리의 결과물이 N개인 경우 사용. 반환 타입은 List<타입>.exists : 쿼리 결과가 존재하는지를 확인. 반환 타입은 boolean.count : SQL의 결과 개수 반환. 반환 타입은 long.By 뒤에는 필드 이름이 들어갑니다. 또한 이 필드들은 And 나 Or 로 조합될 수 있습니다.findAllByNameAndAge 작성하게 되면, select * from user name = ? and age = ? 쿼리가 수행됩니다.findAllByNameOrAge 작성하게 되면, select * from user name = ? or age = ? 쿼리가 수행됩니다.동등 조건 ( = ) 외에 다양한 조건을 활용할 수도 있습니다. 크다 작다를 사용할 수도 있고, 사이에 있는지 확인할 수도 있습니다. 또한 특정 문자열로 시작하는지 끝나는지 확인할 수도 있습니다.GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이StartsWith : ~로 시작하는EndsWith : ~로 끝나는예를 들어 특정 나이 사이의 유저를 검색하고 싶다면, 다음과 같은 함수를 만들 수 있습니다.public interface UserRepository extends JpaRepository<User, Long> { List<User> findAllByAgeBetween(int startAge, int endAge); }JPA와 Spring Data JPA를 활용하여 SQL을 직접 사용해야 하는 아쉬움을 해결 했습니다. 하지만 아직 Service 계층의 역할이 남아 있습니다. 서비스 계층의 중요한 역할은 바로 ‘트랜잭션’ 관리이다. 다음 시간에는 트랜잭션이 무엇인지 그리고 왜 필요한지 알아보도록 하자.27강. 트랜잭션 이론편트랜잭션이란 여러 SQL을 사용해야 할 때 한 번에 성공시키거나, 하나라도 실패하면 모두 실패시키는 기능입니다. 그래서 트랜잭션을 ‘쪼갤 수 없는 업무의 최소 단위’라고 표현합니다. 트랜잭션을 시작하고 사용한 SQL을 모두 취소하고 싶다면, commit 대신 rollback 이라는 명령어를 사용하면 됩니다.다음 시간에는 트랜잭션을 어떻게 적용할 수 있을지 알아보도록 합니다.28강. 트랜잭션 적용과 영속성 컨텍스트트랜잭션을 UserService 에 적용하고 JPA에 등장하는 영속성 컨텍스트라는 개념에 대해 학습합니다.지난 시간에 살펴보았던 것처럼, 우리가 원하는 것은서비스 메소드가 시작할 때 트랜잭션이 시작되어,서비스 메소드 로직이 모두 정상적으로 성공하면 commit 되고,서비스 메소드 로직 실행 도중 문제가 생기면 rollback 되는 것 입니다.트랜잭션을 적용하는 방법은 매우 간단합니다! 대상 메소드에 @Transactional 어노테이션을 붙여주기만 하면 됩니다. 주의할 점으로는 org.springframework.transaction.annotation.Transactional 을 붙여야 합니다. 다른 패키지의 @Transactional 을 붙이면 정상 동작하지 않을 수 있습니다.@Transactional public void saveUser(UserCreateRequest request) { userRepository.save(new User(request.getName(), request.getAge())); }@Transactional public void updateUser(UserUpdateRequest request) { User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName());    userRepository.save(user); }public void deleteUser(String name) { User user = userRepository.findByName(name); if (user == null) { throw new IllegalArgumentException(); } userRepository.delete(user); }@Transactional(readOnly = true) public List<UserResponse> getUsers() { return userRepository.findAll().stream() .map(UserResponse::new) .collect(Collectors.toList()); }데이터의 변경이 없고, 조회 기능만 있을 때는 readOnly 옵션을 줄 수 있습니다.@Transactional(readOnly = true)트랜잭션 적용이 성공적으로 모두 잘 됐는지 테스트를 수행합니다.@Transactional public void saveUser(UserCreateRequest request) { userRepository.save(new User(request.getName(), request.getAge())); throw new IllegalArgumentException(); }@Transactional 어노테이션에 대해 한 가지 알아두어야 할 점은, Unchecked Exception에 대해서만 롤백이 일어난다는 점입니다. IOException과 같은 Checked Exception에서는 롤백 이 일어나지 않습니다.영속성 컨텍스트란, 테이블과 매핑된 Entity 객체를 관리/보관하는 역할을 수행합니다. 스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨 나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료됩니다. 또한, 영속성 컨텍스트는 특별한 능력을 4가지 가지고 있습니다.변경 감지 (Dirty Check) 영속성 컨텍스트에 등록된 Entity는 명시적으로 save 를 해주지 않더라도 알아서 변경을 감지하여 저장쓰기 지연 영속성 컨텍스트에 의해 트랜잭션이 commit 되는 시점에 SQL을 모아서 한 번만 쿼리를 수행( update, delete 동일)1차 캐싱 ID를 기준으로 Entity를 기억하는 기능으로 영속성 컨텍스트가 보관하고 있는 데이터를 활용29강. Section 4 정리. 다음으로!문자열 SQL로 구성했던 우리의 데이터 접근 기술을 객체 지 향 프로그래밍이 가능하도록 JPA를 활용해 완전히 변경했습니다. 이 과정에서 아래의 내용들을 익힐 수 있었습니다.문자열 SQL을 직접 사용하는 것의 한계를 이해하고, 해결책인 JPA, Hibernate, Spring Data JPA가 무엇인지 이해한다.Spring Data JPA를 이용해 데이터를 생성, 조회, 수정, 삭제할 수 있다.트랜잭션이 왜 필요한지 이해하고, 스프링에서 트랜잭션을 제어하는 방법을 익힌다.영속성 컨텍스트와 트랜잭션의 관계를 이해하고, 영속성 컨텍스트의 특징을 알아본다.30강. 책 생성 API 개발하기먼저 요구사항을 살펴봅니다.도서관에 책을 등록할 수 있다.다음으로 API 스펙을 확인합니다.HTTP Method : POSTHTTP Path : /bookHTTP Body (JSON){ "name": String // 책 이름 }결과 반환 X (HTTP 상태 200 OK이면 충분합니다.)book 테이블을 설계하고, Book 객체를 만들고, Repository, Service, Controller, DTO를 만들어 주면 됩니다. 꼭 이 순서로 진행해야 하는 것은 아닙니다. 작업하다 보면 익숙한 순서가 생기게 됩니다.테이블create table book( id bigint auto_increment, name varchar(255), primary key (id) );엔티티@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null;     @Column(nullable = false) private String name; }리포지토리public interface BookRepository extends JpaRepository<Book, Long> {}RookCreateRequestpublic class BookCreateRequest { private String name; public String getName() { return name; } }BookController@RestController public class BookController {     private final BookService bookService;     public BookController(BookService bookService) { this.bookService = bookService; }     @PostMapping("/book") public void saveBook(@RequestBody BookCreateRequest request) { bookService.saveBook(request); } } ​@Service public class BookService {        private final BookRepository bookRepository;     public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } ​    @Transactional public void saveBook(BookCreateRequest request) { bookRepository.save(new Book(request.getName())); } }@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null;     @Column(nullable = false) private String name;        // Book.java 안에 추가된 로직 protected Book() { } ​    public Book(String name) { if (name == null || name.isBlank()) { throw new IllegalArgumentException(String.format("잘못된 name(%s)이 들어왔습니다", name)); } this.name = name; } }이 과정에서 필요한 Book의 생성자가 자연스럽게 생성됩니다. 다음 시간에는 이어서 대출 기능을 구현합니다.31강. 대출 기능 개발하기요구사항사용자가 책을 빌릴 수 있다.다른 사람이 그 책을 진작 빌렸다면 빌릴 수 없다.API 스펙HTTP Method : POSTHTTP Path : /book/loanHTTP Body (JSON){ "userName": String "bookName": String }결과 반환 X (HTTP 상태 200 OK이면 충분합니다.)테이블create table user_loan_history ( id bigint auto_increment, user_id bigint, book_name varchar(255), is_return tinyint(1), primary key (id) )엔티티@Entity public class UserLoanHistory { @Id @GeneratedValue(strategy = IDENTITY) private Long id; private long userId; private String bookName; private boolean isReturn; }is_return 필드는 tinyint입니다. 이를 boolean에 메핑하게 되면 true인 경우 1, false인 경우 0이 저장됩니다.리포지토리public interface UserLoanHistoryRepository extends JpaRepository<UserLoanHistory, Long> {}BookLoanRequest DTO// DTO public class BookLoanRequest { private String userName; private String bookName;     public String getUserName() { return userName; }     public String getBookName() {        return bookName; } }컨트롤러 - loanBook 메서드 추가// Controller (BookController.java) @PostMapping("/book/loan") public void loanBook(@RequestBody BookLoanRequest request) { bookService.loanBook(request); }서비스@Transactional public void loanBook(BookLoanRequest request) {}우선은 책 객체를 이름을 가져옵니다. 만약 책이 없는 경우에는 예외를 던져주어야 합니다. 이름을 기준으로 책을 가져오려면, BookRepository 에 메소드 시그니처 작성도 필요합니다.// Repository public interface BookRepository extends JpaRepository<Book, Long> { Optional<Book> findByName(String bookName); } ​ // Service @Transactional public void loanBook(BookLoanRequest request) { Book book = bookRepository.findByName(request.getBookName()) .orElseThrow(IllegalArgumentException::new); }Book 객체를 가져왔다면, DB에 존재하는 책입니다. 그리고 Book 객체의 책이 누군가 대출 중인지 확인합니다. 이번에는 UserLoanHistoryRepository 에 메소드 시그니처 작성이 필요합니다.public interface UserLoanHistoryRepository extends JpaRepository<UserLoanHistory, Long> {    boolean existsByBookNameAndIsReturn(String bookName, boolean isReturn); }existsByBookNameAndIsReturn의 매개변수로 책 이름과 false 를 넣은 값이 true가 나왔다는 의미는 현재 반납되지 않은 대출 기록이 있다는 의미이니 누군가 대출했다는 의미입니다. 따라서 Service는 다음과 같이 변경됩니다.@Service public class BookService {     private final BookRepository bookRepository;     // UserLoanHistoryRepository에 접근해야 하니 의존성을 추가해주었다! private final UserLoanHistoryRepository userLoanHistoryRepository;     // 생성자에서 스프링 컨테이너를 통해 주입받도록 하였다. public BookService(BookRepository bookRepository, UserLoanHistoryRepository userLoanHistoryRepository) { this.bookRepository = bookRepository; this.userLoanHistoryRepository = userLoanHistoryRepository; }     // 저장 로직 생략 @Transactional public void loanBook(BookLoanRequest request) { Book book = bookRepository.findByName(request.getBookName()) .orElseThrow(IllegalArgumentException::new);         // 추가된 로직, user_loan_history를 확인해 예외를 던져준다. if (userLoanHistoryRepository.existsByBookNameAndIsReturn(book.getName(), false)) { throw new IllegalArgumentException("진작 대출되어 있는 책입니다"); } } }if 문이 실행되지 않으면 대출되지 않은 책이라는 뜻입니다. 따라서 대출 기록을 쌓아주면 됩니다. 이때 userId 가 필요하기 때문에 유저 객체를 가져온 후 UserLoanHistory 를 저장합니다. UserRepository 에 대한 의존성도 새로 필요하고, UserRepository 의 로직도 변경이 필요하며, UserLoanHistory 에 새로운 생성자도 필요합니다. 최종적인 Service 코드는 다음과 같습니다.@Service public class BookService {        private final BookRepository bookRepository; ​    private final UserLoanHistoryRepository userLoanHistoryRepository; ​    private final UserRepository userRepository; ​    public BookService(        BookRepository bookRepository,        UserLoanHistoryRepository userLoanHistoryRepository,        UserRepository userRepository ) {        this.bookRepository = bookRepository;        this.userLoanHistoryRepository = userLoanHistoryRepository;        this.userRepository = userRepository;   } ​    // 저장 로직 생략 @Transactional public void loanBook(BookLoanRequest request) { Book book = bookRepository.findByName(request.getBookName()) .orElseThrow(IllegalArgumentException::new);         if (userLoanHistoryRepository.existsByBookNameAndIsReturn(book.getName(), false)) { throw new IllegalArgumentException("진작 대출되어 있는 책입니다"); }         User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalArgumentException::new);         userLoanHistoryRepository.save(new UserLoanHistory(user.getId(), book.getName())); } }다음 시간에는 마지막 요구사항인 반납 기능을 개발합니다.32강. 반납 기능 개발하기요구사항사용자가 책을 반납할 수 있다.API 스펙HTTP Method : PUTHTTP Path : /book/returnHTTP Body (JSON){ "userName": String "bookName": String }결과 반환 X (HTTP 상태 200 OK이면 충분합니다.)BookReturnRequest DTOpublic class BookReturnRequest { private String userName; private String bookName;    public String getUserName() { return userName; } ​    public String getBookName() { return bookName; } }컨트롤러 - returnBook 메서드 추가@PutMapping("/book/return") public void returnBook(@RequestBody BookReturnRequest request) { bookService.returnBook(request); }서비스 - returnBook 메서드 추가@Transactional public void returnBook(BookReturnRequest request) { User user = userRepository.findByName(request.getUserName()) .orElseThrow(IllegalArgumentException::new);     UserLoanHistory history = userLoanHistoryRepository.findByUserIdAndBookName(user.getId(), request.getBookName()) .orElseThrow(IllegalArgumentException::new);     history.doReturn(); }User 와 UserLoanHistory 가 직접 협업할 수 있게 처리하도록 변경할 수 있지 않는지에 대해 다음 시간에 그 방법을 학습합니다.2주차 미션강의에서 학습한 범위 내에서 미션을 풀어내는 것을 목표로 진행했습니다. 학습 효과를 높이기 위해 어떠한 자료 혹은 검색 없이 스스로 문제 해결을 하려고 노력하고 한 줄마다 의미를 명확하게 이해하고 적용했습니다.여섯 번째 과제! (진도표 6일차)Memory 방식을 제외하고 MySQL 로 동작하도록 구현한다.FruitMySqlRepositoryEx06 리포지토리에 우선 순위를 부여하기 위해 @Primary 를 작성한다.서비스에서 예외 처리를 수행. 리포지토리 새로 추가된isSalesFruitNotExist 메서드로 데이터가 있는지 확인한다.나머지 코드들은 분리한 형태로 코드 분리된 결과입니다. 다음 링크에서 과제 코드를 확인할 수 있습니다.깃허브 링크로 이동하기일곱 번째 과제! (진도표 7일차)과제 #7 제출 스레드 에서 각 코드에 대해 자세히 살펴볼 수 있습니다. 아래 링크는 각 커밋 메세지와 함께 구현한 코드입니다.과제 7 문제 Controller 구현문제 7 문제 Repository 구현문제 7 문제 Service 구현문제 7 문제 Request, Response 구현

백엔드SpringJava

또니

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

[2주차 학습 내용]벌써 스터디를 시작한지 2주차가 되어간다.2주차에는 컨테이너가 무엇인지, 어떻게 동작하는지 JPA 사용해보기, 트랜잭션그리고 조금 더 객체지향 적으로 코드 리팩토링 하는 방법을 배워 보았다.[2주차 과제]이번 2주차에는 기존에 Controller에서 그대로 SQL을 작성했던 것을 Controller - Service - Repository로 분리해보았고,Repository에 SQL을 사용하는 것이 아닌 JPA를 사용하여 DB와 통신할 수 있도록 해보았다.6일차 과제 보기 - https://ddonydev.tistory.com/807일차 과제 보기 - https://ddonydev.tistory.com/81[느낀점]다른 스프링 강의를 들었을 땐 완벽하게 이해가 되지 않고 넘어 갔었고, 정확하게 어떤 일을 하는 지 어떻게 동작 하는지 와닿지 않았었다.하지만 이번 강의를 들으면서 정말 완벽하진 않더라도 어느정도 이해가 되었고, 미션과 병행하며 코드를 쳐보니 조금 더 이해가 수월 했던 것 같다. 또, 그동안은 내가 A라고 알고 있던 것이 전혀 다르게 동작할때 또는 에러가 날때 등 그냥 넘어가거나 에러만 잡기 급급했는데왜 에러가 났는지, 또 왜 전혀 다르게 동작 했는지 이 메서드는 어떤 역할을 하는지 등등 어떻게 궁금해하고 어떻게 찾아가야하는지생각하는 능력이 성장한 것 같아 너무 기뻤다. 이번에 처음 써본 JPA는 너무 어려웠지만 조금 더 공부해본 뒤 미니프로젝트를 진행해 볼 예정이다.이번 주는 웹 개발의 전반적인 흐름과 생각하는 능력을 성장시킬 수 있어서 뜻깊은 한 주였다. 강의 링크 👉 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

백엔드백엔드0기인프런인프런워밍업스터디

도롱이

[인프런 워밍업 클럽_0기] 2주차, 두 번째 발자국 #2

2주차, 두 번째 발자국2주차는 계속 되는 야근으로 인해 시간에 쫓기듯이 공부하고, 과제했던 주였다. 속도가 급하면 체하기 마련! 그래서 오타 등의 실수도 많이해서 삽질로 많은 시간을 보내기도 했다.이전 발자국에서도 남겼지만 강의 별 요약본은 notion에 있다.https://abalone-copper-ebe.notion.site/d2e9b3e27b3348abbde60994cf627ebd?pvs=4 Day 7. 스프링 컨테이너의 의미와 사용 방법스프링 컨테이너의 역할을 정확히알게 되었다.스프링 컨테이너 : 서로 필요한 관계(의존 관계)에 있는 스프링 빈을 연결해주는 역할스프링 컨테이너의 동작 과정을 알게 되었다.스프링이 서버를 시작한다 > 스프링 컨테이너가 시작된다 > 기본적으로 설정된 스프링 빈들이 등록된다 > 개발자가 설정한 스프링 빈이 등록된다 > 필요한 의존성이 자동으로 설정된다.스프링에서 등록한 스프링 빈을 어떤식으로 땡겨와야하는지 알게 되었다.등록된 스프링 빈을 사용하려면 해당 클래스 파일을 스프링 빈에 등록해야 땡겨올 수 있다!Interface를 이용하여 코드 수정 시 수정하는 부분을 최소화 할 수 있다.다른 클래스에서 Interface를 상속받게하고 생성자에서 교체하고 싶은 구현체를 변경만해주면 되기 때문!하지만 위의 방법도 어찌저찌 됐든 생성자를 수정해야하기 때문에 수정은 불가피하다! 이런 걸 해결할 수 있는 어노테이션이 @Primary 와 @Qualifier를 사용하면 구현체 주입 우선순위를 설정해줄 수 있다.참고로 Primary와 Qualifier 중 우선순위가 더 높은 건 Qualifier이다.  과제https://devhan.tistory.com/323기존에 작성했던 코드를 Controller, Service, Repository 3단계로 분리하는 과제였다. 다른 건 다 괜찮았는데 Repository가 Interface로 변경되고 MemoryRepository와 MySQLRepository로 구현체를 나뉘어서 개발하는 부분에 시간을 너무 많이 쏟았던 기억이 난다.그래도 개발하면서 기존에 작성했던 코드를 리팩토링 하는 과정도 거칠 수 있어서 더 재밌게 코딩할 수 있었던 거 같다. Day 8. Spring Data JPA를 사용한 데이터베이스 조작8일차에는 String으로 작성했던 쿼리 방식의 단점을 알아보고 변경하는 시간이었다.Java의 객체와 Database의 테이블의 패러다임이 다르다는걸 정확하게 알게되었다.또한 Java의 상속을 Database로 표현하기 어려운 부분이 존재한다는걸 알게되었다.JPA는 단순한 규칙이라는걸 알게되었다.JPA의 전반적인 설정 방법과 사용 방법들을 알게되었다.람다식이 우르르 나와서 해당 관련 공부를 또 뼈저리게 느꼈다. 과제https://devhan.tistory.com/324이번 과제도 기존에 작성했던 코드 중 문자열로 작성된 쿼리를 JPA로 변경하는 과제였다.warehousingDate 필드가 제대로 맵핑되지 않는 문제때문에 골머리를 앓았다. 찾아보니 스프링에서는 카멜케이스로 작성된 필드는 DB 컬럼에서는 '_'로 단어를 구분한다고 한다. ex) warehousingDate -> warehousing_date이 에러는 ddl-auto를 create로 만들어주고 처음부터 다시 매핑해서 해결되었다.그 외에는 딱히 어려운 점은 없었다! Day 9. 트랜잭션과 영속성 컨텍스트트랜잭션의 이론에 대해서 알게 되었다. 트랜잭션이 뭘 하는지는 대략적으로 알았는데 딱 정의할 수는 없었다. 트랜잭션 -> 쪼갤 수 없는 업무의 최소 단위@Transactional의 사용 범위와 readOnly 옵션에 대해 알게 되었다.Select 쿼리에서는 readOnly 옵션을 사용하는게 성능상 더 좋다영속성 컨텍스트에 대해 알게 되었다.테이블과 매핑된 엔티티 객체를 관리하고 보관스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨남트랜잭션을 종료하면 영속성 컨텍스트가 종료됨영속성 컨텍스트의 4가지 장점변경 감지(Dirty Check) : 영속성 컨텍스트 안에서 불러와진 객체는 명시적으로 save 하지 않더라도, 기존 정보와의 변경을 감지해 자동으로 저장된다.쓰기 지연 : DB의 INSERT, UPDATE, DELETE SQL을 바로바로 날리는게 아니라 트랜잭션이 commit될 때 한번에 모아서 한 번만 날린다.1차 캐싱 : 아이디를 기준으로 객체를 기억하고, 해당 객체가 DB에서 다시 불려와질 때 변경된 정보가 없다면 굳이 DB를 통하지 않고 영속성 컨텍스트에서 해당 정보를 돌려준다.캐싱된 객체는 완전히 동일하다 -> 객체의 인스턴스 주소까지 완전 동일 과제이 날부터 미니 프로젝트가 시작되었다.첫 날이라 유의미한 코딩을 한 건 아니었고 프로젝트 생성부터 설정까지 완료하였다.그리고 앞으로 어떤식으로 개발할지 전체적인 틀을 그렸던 것 같다! Day 10. 조금 더 복잡한 기능을 API로 구성하기책 생성, 대출, 반납 기능을 개발하면서 전체적인 개념을 다시 잡기도하고 좀 더 객체지향적으로 코딩하는 방법을 알았던 날이다.되게 좋았던 건 얼마 전에 객체지향의 사실과 오해라는 책을 읽었었는데 사실 책에서는 예시 코드가 나오지 않아 약간 실무에서는 어떤식으로 접근해야할까에 대한 의문이 좀 남아있었다. 책을 읽어도 약간 반만 알겠는 느낌..? 근데 이 강의에서 딱 객체지향적으로 코딩을 해볼 수 있어서 그래도 느낌을 좀 알 것 같았다. 평소 회사에서 전혀 객체지향적으로 개발하지 않아 domain에 비즈니스 로직을 직접 넣어도 되는지 조차 몰랐었는데 이번 강의에서 그 부분에 대한 갈증을 조금 해소할 수 있어서 아주 마음에 들었다. 과제https://devhan.tistory.com/326어느정도 개발이 끝났던 시기였던 거 같다. 과제를 하면서 Team과 Employee의 연관관계를 담은 테이블을 강의처럼 따로 만들어야할지 말지 고민이 많았었고 또, Team의 ID를 Long 타입의 id로 하느냐, String 타입의 name으로 하느냐의 고민도 많았다..! Long 타입의 id로 하면 나중에 Employee를 조회할 때마다 Team의 객체를 가져와서 name을 또 Response 객체에 저장해줘야할 것 같은 번거로움 때문에 그냥 String name을 primary key로 지정했다. 코드에 대한 많은 생각을 할 수 있어서 좋은 시간이었던 거 같다.   

백엔드워밍업클럽백엔드스터티