🎁 모든 강의 30% + 무료 강의 선물🎁

블로그

msj09252

[인프런 워밍업 클럽 스터디 2기] 백엔드 클린 코드, 테스트 코드 1주차 발자국

해당 글은 박우빈님의 강의를 수강하며 작성한 글입니다. 강의 수강1. 추상과 구체추상과 구체, 이 두 단어는 항상 같이 다니는 한 쌍이다.추상이란?구체적인 정보에서 어떤 이미지를 뽑아내는 것.중요한 정보는 가려내어 남기고 덜 중요한 정보는 생략하여 버린다.추상은 항상 구체적인 실재에서 시작해야 한다.위 내용을 통해 구체적인 내용에서 추상의 단계로 가는 방법이 있고, 그 반대의 방법도 존재한다.우리는 강의를 통해서 구체적인 지뢰찾기 코드를 추상화해가며 객체지향의 개념을 적용해보는 시간을 가졌다.  2. 논리, 사고의 흐름뇌 메모리 적게 쓰기최소한의 인지적 노력으로 최대의 정보를 제공해야 한다.뇌는 범주화라는 작업을 통해 무언가를 떠올린다.이 말은 즉, 누군가 작성한 코드(조상)가 남(후손)이 보아도 쉽게 읽고 이해할 수 있다면, 그것은 뇌 메모리를 적게 쓸 수 있게 한 효과적인 코드가 아닐까 싶다.Early returnEarly return 사용을 통해 else의 사용을 지양하자사고의 depth 줄이기공백 라인공백 라인도 의미를 가진다.읽는 사람으로 하여금 의미를 분절시켜 추가적인 정보전달이 가능하다.부정어 사용부정어구를 쓰지 않아도 되는 상황인지 체크부정의 의미를 담은 다른 단어가 존재하는지 고민하거나 부정어구 메서드 사용해피 케이스와 예외 처리 3. 객체 지향 패러다임객체 설계하기 : 미션으로 대체SOLIDSRP: Single Responsibility PrincipleOCP: Open-Closed PrincipleLSP: Liskov Substitution PrincipleISP: Interface Segregation PrincipleDIP: Dependency Inversion Principle 4. 객체 지향 적용하기상속과 조합상속보단 조합을 이용하자.조합과 인터페이스를 활용하는 것이 유연한 구조다.Value Object훌륭한 추상화 기법 중 하나기본 타입을 객체로 감싸서 의미 부여 및 추상화를 하는 것.동등성 vs 동일성일급 컬렉션Enum의 특성과 활용상수집합변경이 잦은 개념은 Enum보다 DB로 관리하자.다형성 활용하기숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것이다.미션Day 2추상과 구체의 예시 제시추상"노래를 듣는다" 라는 추상적인 문장을 구체화 해보았다구체이어폰에서 발생한 음파가 공기를 통해 외이로 도달한다.외이도를 통해 전달된 음파는 고막에 도달하고, 고막은 음파에 의해 진동한다.고막의 진동은 중이의 세 개의 작은 뼈로 전달된다.세 개의 뼈는 음파의 진동을 증폭시켜 내이의 달팽이관으로 전달한다.달팽이관에 있는 액체가 움직이면서 유모 세포를 흔들고, 이 과정에서 전기 신호로 생성한다.전기 신호가 청각 신경을 통해 뇌로 전달된다.뇌의 청각 피질이 신호를 분석하고 해석하여 소리를 인식한다.Day 41. 코드 리팩토링2. SOLID에 대한 자기만의 언어로 정리깃허브 링크 대체노션 링크 대체회고클린코드란 무엇인가 에 대해 다시 한번 고민하게 되는 주간이었다. 사이드 프로젝트를 진행하면서 주로 나의 코드를 내가 자주 보고 고치기 때문에 남이 어떻게 이해할지에 대해서 깊게 고민해보지 못했던 것 같다. 이번 기회를 통해 프로젝트 코드에서 고칠 부분이 상당히 많이 떠올랐고, 스터디 중간 중간 적용해보려고 노력하려 한다. 빡빡한 스케줄을 회사와 병행하다보니 강의 수강과 미션 수행을 모두 처리하는 것이 상당히 쉽지 않았다. 다행히 이번 주간에는 쉬는 날이 많아서 겨우 맞춰나간 것 같다. 많은 스터디원 분들이 성실히 수행하는 것을 보며 동기부여가 되고 있다.남은 3주도 잘 마무리하여 완주러너가 되고 싶다.

BE클린코드워밍업클럽

lch9502

워밍업 클럽 3기 BE - 발자국 2주 차

1. 들어가며어쩌다보니 2추 차까지 왔네요? 이번주는 일이 많아서 정신없이 보냈습니다.게다가 클린코드 리팩토링 실습 미션이 있었습니다.아쉽게도 리팩토링을 깔끔하게 마무리 짓지 못해서 코드 리뷰 신청은 못했네요.. 후회할 일이 늘어났습니다... 😢어쨌든 미션 덕분에 지난주 강의를 꽤 여러번 반복해서 들었는데요, 생각보다 더 많은 인사이트를 얻었습니다.그리고 갈 길이 참 멀다는 사실도 알게 되었죠 ㅎ... 2. 학습했던 내용 나만의 키워드로 작성하기섹션 6. 코드 다듬기좋은 주석주석이 많다는 것은 가독성이 좋지 않은 코드주석이 필요한 경우는 히스토리를 알 수 없을 경우변수와 메서드 나열 순서변수는 사용하는 순서대로공개 메서드를 상단에 위치하고, 비공개 메서드 하단에 위치패키지 나누기기능 유지보수하기기능 유지보수하기정렬 단축키, linting, style - sonarlint, editorconfig   섹션 7. 리팩토링 연습Optionalreturn null / Optional 파라미터 사용은 안티패턴객체에 메시지 보내기공개 메서드를 통해 객체끼리 협력하기객체의 책임과 응집도 - IO 통합, 일급컬렉션, display(), Order 추출추상화 관점의 차이 - FileHandler구현에 초점을 맞춘 추상화 VS 도메인 개념에 초점을 맞춘 추상화  섹션 8. 기억하면 좋은 조언들능동적 읽기 오버 엔지니어링구현체가 하나인 인터페이스너무 이른 추상화은탄환은 없다클린 코드도 은탄환 x항상 정답인 기술은 없음. 경험이 중요! 섹션 3. 단위 테스트수동 테스트, 자동화 테스트 Junit5, AssertJ단위 테스트: 작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트Junit5: 단위 테스트를 위한 프레임워크AssertJ: 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리해피 케이스, 예외 케이스항상 예외 케이스가 있는지 고민하고 테스트를 작성해야 함경계값 테스트먼저 테스트 케이스를 세분화하고 경계값이 존재하는 경우는 경계값에서 항상 테스트를 할 수 있도록 고민을 하는게 중요테스트하기 쉬운/어려운 영역 (순수함수)관측할 때마다 다른 값에 의존하는 코드우리가 작성한 코드가 외부 세계에 영향을 주는 코드 섹션 4.TDD: Test Driven DevelopmentTDD테스트를 먼저 작성한 후에 그를 통해 기능을 만들고 그 다음에 기능을 수정할 때 테스트의 도움을 받을 수 있도록 하는 개발 방법론 중에 하나레드 - 그린 - 리팩토링레드: 실패하는 테스트를 먼저 작성하는 단계그린: 테스트가 통과할 수 있는 최소한의 코딩을 하는 단계리팩토링: 테스트를 통과하는 것을 유지하면서 구현 코드를 개선하는 단계섹션 5. 테스트는 []다@DisplayName - 도메인 정책, 용어를 사용한 명확한 문장DisplayName을 섬세하게!1. 명사의 나열보다 문장으로2. 테스트 행위에 대한 결과까지 기술하기3. 도메인 용어를 사용하여 한층 추상화된 내용을 담기4. 테스트의 현상을 중점으로 기술하지 말 것Given / When / Then - 주어진 환경, 행동, 상태 변화Given : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등) When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증TDD vs BDD 3. 학습 회고컴퓨터 공학과를 나왔고 실무에서 몇 년을 일했지만 객체 지향으로 코드를 작성하는 방법은 배운적이 없는 것 같습니다.캡, 추, 상, 다가 뭔지는 알고, SOLID가 뭔지는 알지만 코드에 잘 적용이 됐는지는 항상 의문이였죠.이 강의는 정말 기대했던 것 보다 더 많은 인사이트를 얻는 강의였습니다. 아마 당분간은 몇 번 더 돌려볼 것 같네요.그래도 지금까지 여러 블로그나 책들을 읽으면서 객체 지향적으로 코드를 작성하려고 노력했었는데요, 강의와 어느정도 결이 비슷하더라구요. 가는 길이 얼추 올바른 길이였다는 사실이 안심이 됩니다. 테스트 코드 강의는 정말 빠르게 봤던 것 같습니다. 강의가 출시되자마자 바로 봤었죠.그 때는 테스트 코드를 잘 작성하는 게 뭔지 모르기도 했고, 주변에서도 작성하는 사람이 단 한명도 없었기에 정말 도움이 되는 강의였습니다.이번에 속도 3배로 다시 쭉 훑었는데 처음 들었을 때 생각이 많이 났습니다.당시에는 몰랐는데 지금은 테스트 코드를 작성하는데 부담이 없는 것을 보니 성장하긴 했나봅니다. ㅋㅋ  4. 미션 회고미션 Day 7개인적으로 이번 미션에 대해 나에게 점수를 매긴다면 좋은 점수를 주지 못할 것 같네요.강의에서 배운 내용을 제대로 적용하지 못했다는 생각이 듭니다.(변명을 해보자면 시간이 너무 부족했다는... 💦💦) 저는 인터페이스로 빼서 구현하는 방법으로 코드를 꽤 많이 작성합니다. 그리고 객체를 조합으로 풀어서 코드를 작성하는 것을 선호합니다.그래서 리팩토링이 쉬울 줄 알았는데요, 강의 내용을 어떻게든 쑤셔넣으려고 하니까 잘 안되더라구요.. 😭아직은 이론과 실전에 갭이 있다는 뜻이겠죠?더 열심히 해야겠습니다!! 그리고 금요일에 진행된 코드 리뷰는 재미있었습니다. 😆😆😆딴 사람들이 작성한 코드를 편하게 리뷰하는 우빈님한테 감탄했던 것 같네요.역시 세상은 넓고 고수는 많다는?

BE워밍업클럽3기리팩토링테스트코드

[워밍업클럽3기] Backend 1주차 발자국

Section 02. 추상추상: 뽑을 추 & 코끼리 상(형상 상)구체적 정보에서 어떠한 이미지를 뽑아냄wiki정의 : 사물을 정확하게 이해하기 위해서 사물이 지니고 있는 여러가지 측면 가운데서 특정한 측면만을 가려내어 포착하는 것이다. 어떤 일면만을 추상하는 것은 다른 측면을 버린다는 것과 같다.추상은 항상 구체적인 실재에서 시작해야한다..추상화 과정.. 빅뱅이론에서 쉘든은 추상화가 덜되었고, 보통사람은 추상화가 잘된느낌??추상화가 덜되어서 자꾸 뭐든 설명하려들고, 나머지사람들은 추상화가 잘되어 그렇게까지 디테일하게 말하지 않는 느낌? 예시) 추상: 너 만나기 전에 배가 고파서 먼저 밥먹었는데, 너는 밥 먹었니? 구제: 너와 00시에 만나기로 하였는데, 00시부터 나의 위에서 아무것도 없기 때문에 내 뇌로 배고프다는 신호를 보내주었기 때문에 나의 뇌가 무언가를 먹어야 한다는 신호를 보내어 집안의 냉장도를 열어서 음식을 찾아서 내 입에서 저작작용을 거쳐 식도를 지나 위로 넘어가는 일(=밥먹음)을 하고 왔는데, 너도 그런 일(=너도 먹음?)을 하고 왔니?추상: 심플, 구제: 구구절절 ?추상: 핵심만 담아 짧고 간결하게 표현하는 것. (효율 극대화)구제: 하나하나 풀어서 최대한 자세하게 설명하는 것. (비효율적)Method: 두개이상의 일을 하지 않도록 노력할 것!! 생략할 정보와 의미를 부여하고 드러낼 정보 구분해야함반환 타입 매서드명 (파라미터) {매서드구현부}<method name: 매서드명>추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름파라미터와 연결지어 더 풍부한 의미를 전달 할 수도 있음.<Parameter: 파라미터>파라미터의 타입, 개수, 순서를 통해 의미 전달외부세계와 소통하는 창임!외부세계로 method를 작성한 사람이 읽는 사람, 사용하는 사람으로 하여금 내가 뭘 할지 보여주는 것.<반환타입>method signature에 납득이 가는, 적절한 타입의 반환값 돌려주기.반환타입이 boolean의 경우 참거짓임.void 대신 충분히 반환할 만한 값이 있는지 생각해 볼것.반환값이 있다면 테스트가 용이해짐.void = 반환값이 없음을 의미. "값을 반환하지 않는다"의 의미.추상화는 코드의 양이 중요하다기 보다는 의미가 중요함!!! Section 03. 논리, 사고의 흐름사고의 depth 줄이기보이는 depth를 줄이는 것이 아니라 "사고과정의 depth"를 줄이는 것이 중요함.사고과정에 도움이 되면 여러 depth가 있어도 굳이 분리할 필요 없음 선언한 변수와 실제 사용되는 코드 사이에 쓸데없는(=연관되어있지 않은) 코드를 많이 넣지말것쉽게 생각해서 친한친구끼리 붙여두는것. 친한 친구 두명사이에 여러명이 있으면 둘의 대화가 하기 힘들어짐. 공백라인: 라인의 공백을 잘 주면 코드 읽기가 쉬워짐아무거나 붙여놓고, 떨어뜨려놓으면 가독성이 떨어지기 때문에, 의미단위로 자르는 것이 좋음 부정어: 최대한 부정서 사용을 자제할 것.부정의 의미를 담은 다른 단어 유무 확인부정연산자는 가독성이 떨어지기 때문. Section 04. 객체 지향 패러다임추상의 관점으로 보는 객체지향 절차지향: 정해진 순서대로(절차) 처리  객체지향: 객체간의 협력  함수형: 순수함수를 기반으로함.관심사의 분리관심사를 모음 = 유지보수가 쉬워짐높은 응집도 = 낮은 결합도 객체 설계객체가 제공하는것절차지향에서 잘 보이지 않았던 개념을 가시화함관심사 모음캡슐화되어 높은 추상화레벨에서 domain logic을 다룰 수 있음새로운 객체를 만들때 주의할점1개의 관심사인가외부세계와 어떠한 소통을 하려는지 생각할 것생성자, 정적 factory method에서 유효성 검증가능domain에 특화된 검증 로직이 들어갈 수 있음setter and getter 사용자제데이터는 불변이 최고임변하는 데이터라도 객체가 handling할 수 있어야함.객체 내부에서 자체적으로 변경/ 가공으로 처리 할 수 있는가네이밍은 의도를 드러내는 네이밍이 중요함field의 수는 적을 수록 좋음불필요한 데이터가 많다 = 복잡도가 높다 = 대응할 변화가 많다 = 코드가 골치아파진다미리 제공하는 것이 성능상의 이점이 있다 = 필드로 가지고 있는 것이 좋을 수 있음.SOILDSRP: Single Responsibility Principle하나의 클라스 = 하나의 변경이유(= 책임)객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경됨관심사의 분리높은 응집도, 낮은 결합도SRP를 잘지킴 = 겹합도 낮음 = individual workingOCP: Open-Closed Principle개방-폐쇄 원칙확장(=open)과 수정(=closed)즉, 확장은 가능, 수정은 불가능추상화와 다형성을 활용하여 지킬 수 있음.LSP: Liskov Substitution Principleparent class의 instance는 child-class의 instance로 치환가능자식클라스는 부모클라스기능 +aLSP를 위반하면 상속 클라스를 사용할 때 오동작, 예상밖의 예외가 발생하거나 이를 방지하기 위하여 불필요한 타입체크가 동반될 수 있음.ISP: Interface Segregation Principle클라이언트는 사용하지 않는 인터페이스에 의존하면 안됨interface를 잘게 쪼개기!ISP위반 = 불필요한 의존성 = 결합도 높아짐 = 특정 기능의 변경이 여러 클래스에 영향을 미침DIP: Dependency Inversion Principle상위수준의 모듈은 하위 수준모듈에 의존해서는 안됨둘 모두 추상화에 의존의존성의 순방향은 고수준이 저수준 모델을 참조하는것, 역방향은 고수준 저수준 모두 추상화에 의존하는것직접적으로 의존하지 않게 설계됨. Section 05. 객체 지향 적용하기상속과 조합상속 < 조합상속은 수정이 어려움조합 and interface를 활용하는 것이 유연한 구조임상속을 통한 코드의 중복제제가 주는 이점보다, 중복이 생겨도 유연한 구조가 주는 설계적 이점이 더 큼.즉 중복도 상황보며 잘 이용해야함 중복이 무조건 나쁜건 아님Value Object (VO)식별자 없이 내부의 모든 값이 다 같아야 동등한 객체로 취급함. 개념적으로 전체 필드가 다같이 식별자 역할을 한다고 생각해도됨\  기본 타입을 객체로 감싸서 추상화 값으로 취급하기 위해서 불변성, 동등성, 유효성 검증등을 보장해야함.불변성: final field, setter금지동등성: 서로다른 인스턴스여도(동일성이 달라도), 내부의 값이 같이면 같은 값 객체로 취급유효성 검증: 객체가 생성되는 시점에 값에 대한 유효성을 보장하기.Entity식별자가 같음 = 동등한 객체같은 식별자, 필드의 값이 다른 두 인스턴스 = entity가 시간이 지남에 따라 변화할 것으로 예측가능일급 컬렉션일급: 다른 요소에게 가능한 모든 연산자를 지원하는 요소변수할당가능파라미터 전달가능함수의 결과로 반환 가능 일급함수: 함수는 변수에 할당될 수 있고, 인자로 전달가능, 함수의 결과로 함수가 반환될 수 있음일급 컬렉션: 컬렉션만을 유일하게 필드로 가지는 객체다른 객체와 동등한 레벨로 다루기 위함collection 추상화: 의미담을 수 있고, 가공로직의 보금자리가 생기고 테스트 작성도 가능 Enum 특성: 상수의 집함, 상수와 관련된 로직을 담을 수 있는 공간특정 도메인 개념에 대해 그 종류와 기능 표현가능변정이 잦은 개념= DB로 관리하는 것이 나을 수 있음다형성활용변화하는것 : 조건 & 행위 = 구체변화하지 않는것: 조건을 만족하는가? 행위를 수행하는가? = 추상화OCP를 지키기 위해서 a,b는 구별해야함.숨겨져 있는 도메인 개념 도출도메인 지식은 만드는 것이아니라 발견하는것!!!객체지향 = 흉내내는 도구설계할 때는 근시적, 거시적 관점에 따라 최대한 미래를 예측틀렸다는 것을 인지할 경우 돌아올 수 있도록 코드를 짜야함.  Day02.미션추상: 핵심만 담아 짧고 간결하게 표현하는 것. (효율 극대화) 구체: 하나하나 풀어서 최대한 자세하게 설명하는 것. (비효율적) 예시) 추상: 너 만나기 전에 배가 고파서 먼저 밥먹었는데, 너는 밥 먹었니? 구체: 너와 00시에 만나기로 하였는데, 00시부터 나의 위에 아무것도 없었기 때문에, 내 뇌로 배고프다는 신호를 보낸 결과 나의 뇌가 무언가를 먹어야 한다는 신호를 보내어 집안의 냉장고에서 찾은 음식이 내 입에서 저작작용을 거쳐 식도를 지나 위로 넘어가는 일을 하고 왔는데, 너도 그런 일을 하고 왔니? 즉, 보통 사람이 이야기하는 것은 추상화 된 개념이고, 이를 하기 위한 코딩을 하는 것이 구체가 되는 것이 아닐까 생각하게 되었습니다. Josh Darnit 유튜버의 Exact instructions challenge영상에서 아빠는 구체화 된 상태이고 아이들이 추상화 된 방법으로 이야기하는 것이라 생각하게 되었습니다. Day04. 미션 // ; Original public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } else { if (order.getTotalPrice() > 0) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } } return true; }//ordering public boolean validateOrder(Order order) { if (order.getItems().isEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.getTotalPrice() <= 0) { log.info("올바르지 않은 총 가격입니다."); return false; } if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }// 캡슐화 public class Order { public boolean isValid() { return !getItems().isEmpty() && getTotalPrice() > 0 && hasCustomerInfo(); } public String getValidaionMessage { if(getItems().isEmpty()) return "No order list"; if(getTotalPrice() <= 0) return "Wrong Price"; if(!hasCustomerInfo()) return "No user information"; return "Thanks for order." } }// 부정문 제거 public class Order { public boolean isValid() { return hasItems() && getTotalPrice() > 0 && hasCustomerInfo(); } public String getValidationMessage() { if(!hasItems()) return "No order list"; if(getTotalPrice() <= 0) return "Wrong Price"; if(!hasCustomerInfo()) return "No user information"; return "Thanks for order." } public boolean hasItems() { return !getItems().isEmpty(); } public boolean getTotalPrice() { return totalPrice; } public boolean hasCustomerInfo() { return customerInfo() != null; } } SRP: Single Responsibility Principle 하나의 클래스, 하나의 책임 예시) 필름카메라: 필름카메라는 카메라라는 목적에 맞게 사진을 찍는 하나의 기능만을 가지고 있음. OCP: Open-Closed Principle 확장에 대해 열려있고, 수정에 닫혀있음. 예시) 디지털 카메라: 사진을 찍는 목적은 있지만, 여러 렌즈를 교체하며 zoom정도의차이(확장)의 기능을 가지고 있지만, 사진 자체를 수정할 수는 없음. LSP: Liskov Substitution Principle 자식 클라스는 부모 클라스를 대체할 수 있어야 한다. 예시) 스마트폰에 대해 이야기 할 수 있음. 초창기 핸드폰(부모클라스)의 경우 전화와 문자의 기본적인 기능만 있었으나 현재의 스마트폰(자식클라스)는 거기에 추가적으로 카메라, 인터넷접속등의 추가적인 기능이 있어 자식이 부모 클라스를 대체할 수 있다. ISP: Interface segregation Principle 하나의 커다란 인터페이스보다 작고 구체적인 인터페이스가 좋다. 예시) 카메라 기능: 실제 카메라는 사진을 찍는 기술을 가지고 있고, 그 사진을 편집하는 다른 프로그램이 동시에 존재한다. 이 두개를 모두 할 수 있는 것이 스마트폰인데, 이마저도 스마트 폰안의 다른 작은 인터페이스가 구체적이기 때문에 가능한것이다. DIP: Dependency Inversion Principle 고수준 모듈은 저수준 모듈에 의존해서는 안되고, 둘다 추상화된 인터페이스에 의존해야함. 예시) C type의 충전선은 여러 기기를 충전할 수 있음. C type 충전 cable로아이폰, 아이패드, 노트북 모두 충전이 가능함. 즉 추상화된 인터페이스(C type cable)에 각각의 인터페이스(아이폰, 아이패드, 갤럭시, 노트북등)는 각자 구현하는 것임. 마치며...3개 스터디를 한번에 하려는건 역시 무모한 일인가싶어 우선순위를 정했다.FE는 물론 중요하지만 코딩에 시간을 생각보다 많이 써야해서 내 배움 속도에 맞게 하기로 하였다.그래서 BE-CS-FE 순으로 초점을 두기로 하였다. BE야말로 내가 잘 모르는 분야인데 와 Java로 하려니 더 머리가 복잡쓰하다그래도 C#을 배웠던 짬바덕일까, 생각보다 코드 부분에서 나쁘지 않았다.그래도 개념적인 부분에 많은 것이 부족하다 느꼈기 때문에 그런 부분을 다시 review할 수 있는 것이 너무 좋음.특히, solid라는 개념은 대충알고 있었는데 다시한번 짚을 수 있어서 너무 좋음.아 뭐 이런거는 이렇겠지 싶었던 부분도 다시집고 넘어 갈 수 있어서 너무 좋지만 남은 3주가 너무 걱정됨.내가 왜 세개를 했냐고 이렇게 안하면 난 안할테니까...이번주 고생한 나에게 상따윈 없다 담주 더 고생해라! 

백엔드백엔드BE발자국1주차

ykm8864

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

학습 내용리팩토링을 왜할까? 클린코드를 왜 작성할까?우리는 생산을 해냈다고 끝이 아니다. 이후에 조상들이 작성한 코드를 후손들이 유지보수하는 일이 생기기 마련이다. 이에 관련하여 여러가지 방법에 대하여 배워보자추상? 그게 뭔데의미를 담을 수 있는 더 작은 단위, 책임이름짓기단수와 복수의 구분관용어가 아닌 이상 줄임말 자제그 집단만 알 수 있는 은어/방언 사용하지 않기좋은 코드를 많이 보기메서드로의 분리의미를 담을 수 있는 더 작은 단위로 메서드를 분리하기기준 : 추상화되는 의미가 있는가? 이미 이해가 잘 간다면 그대로 두는게 더 나을 수 있다.추상화 레벨 구체에 가깝다 = 추상화 레벨이 낮다. (디테일한 코드 상세) 추상에 가깝다 = 추상화 레벨이 높다. (디테일한 코드를 외부세계로 감싸는 메서드)하나의 세계 안에서는, 추상화 레벨이 동등해야 한다. 내가 너무 주변 레벨에 비해서 구체화시키고 있다면 추상화를 고려해보자매직넘버와 매직스트링의미를 갖고 있으나, 상수로 추출되지 않은 숫자상수를 추출하고 이름을 짓고 의미를 부여함으로서 가독성이 향상될 수 있다.상수로 뺀다는건, 이거는 중요한 숫자야~ 유지보수할때 중요깊게 봐야할 필요가 있을거야~ 라는 의미를 줄 수 있다.  논리 사고의 흐름#최소의 인지적 노력으로 최대의 정보를 제공# 뇌는 한번에 한가지 일 밖에 하지 못한다. 멀티태스킹? 그건 저글링일 뿐이다.뇌에 메모리를 적게 쓰게 하기.Early return논리의 흐름을 빠르게 중단시켜 생각 회로를 단순화 한다.코드 흐름을 단순화하고 가독성을 향상중첩분기, 중첩반복문에 대한 최적화depth를 1로 만들 수 있는지 생각해보기때로는 stream에 대한 활용도 고려할 수 있다.2중 중첩구조 안에도 사고에도움이 된다면 중첩 안에 메서드를 분리하여 두어도 좋다.사용할 변수는 가깝게 선언하기공백라인도 의미를 가진다복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.부정어를 대하는 자세논리를 거꾸로하여 생각하는 과정을 거친다는 것은 가독 저하의 요인일 수 있다.부정어구를 쓰지 않아도 되는 상황인지 체크하기 (무조건 부정조건으로검사해야하는 상황인지)부정의 의미를 담은 다른 단어자체가 존재하는지 고민하기 or 부정어구로 메서드 명 구성해피케이스와 예외 처리예외가 발생할 가능성 낮추기검증로직은 생성자 or 별도의 분리가 필요의도한 예외와 예상하지 못한 예외를 구분하기Null을 대하는 자세equals작성시 상수를 앞에 둔다.Optional은 비싼 객체라 항상 좋은건 아니지만 값이 있을 수도 있고 없을 수도 있는 상황에는 고려할 수 있다. 이때 멤버변수에 Optional은 좋지 않다. 만들어진 목적이 메서드의 반환타입에 쓰게금 설계되어있다.[orElse() : 괄호 안에 값이 항상 실행orElseGet() : 옵셔널안에 원본값이 null인 경우 실행orElseThrow() : 값이 있으면 쓰고 없으면 예외를 던지겠다. 라는 의미라서 그냥 써도된다.StackTrace는 안티패턴이다. 추상의 관점으로 바라보는 객체 지향객체지향 패러다임객체 간의 협력과 객체가 담당하는 책임객체간의 협력과 객체가 담당하는 책임의 관점으로 추상화를 접목시켜 이해하는 것이 중요하다.관심사의 분리관심사끼리 객체를 분리한다.높은 응집도, 낮은 결합도 유지객체 설계하기객체도 추상화와 같이 객체로 나누는 순간 외부와 경계가 발생한다. 이때 외부세계와 소통을 위해서 공개메서드를 활용한다.객체는 외부로 기능을 제공해준다. (개념의 가시화 = 관심사)비공개필드(데이터), 비공개로직(기능 구현부), 공개 메서드 선언부만 외부로 노출하여 이루어진다.여러 객체를 사용하는 입장에서는 구체적인 구현에 신경쓰지 않고 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다.새로운 객체를 만들 때 주의할 점1개의 관심사로 명확하게 책임이 정의되었는지 확인유효성 검증setter 사용 자제(데이터의 불변성)getter가 꼭 필요한지 생각해보기 (객체에 메시지를 보내서 필요한 정보를 메서드로 가져올 수 없는지?)필드의 수는 적을 수록 좋다. 객체지향 패러다임(SOLID)SRP : ”책임을 볼 줄 아는 눈”전문가가 되어야한다. 하나 이상의 일을 책임지게 되면 업무의 효율성이 떨어지게 된다. 이를 어기면 각각의 역할에 집중할 수 없고 공통 업무의 변화가 생기면 모두 바뀌어야한다.OCP : "기존 코드의 변경 없이 , 시슽템의 기능을 확장할 수 있어야한다. (추상화와 다형성의 활용)"전기콘센트 같은것. 허용 전압 규격만 맞으면 냉장고, 가스레인지 등 모든 가전제품을 사용 가능하다. 가전제품이 늘어나도 규격이 맞으면 사용 가능하고 전기콘센트를 교체할 필요 없다.LSP : "자식클래스는 부모클래스의 책임을 준수하며, 부코클래스의 행동을 변경하지 않아야한다."엄마말을 잘 듣자. 상속의 세계에서는 자식은 부모를 이길 수 없다. 따라서, 자식은 부모의 규칙을 어기거나 변경할 수 없이 말을 잘 들어야한다.ISP : "인터페이스를 기능단위로 잘게 쪼개라"덜어냄의 미학. 흑백요리사에서처럼 인터페이스도 많은 기능(책임)을 가진다고 좋은게 아니다. 필요한 만큼만 가지는게 최고다.DIP : "상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다."수준에 맞게 놀자. 고수준(추상 : 핵심비지니스 로직) 모듈이 저수준(구체 : 구현세부사항)모듈을 직접적으로 참조하는 것은 저수준모듈의 변경에 민감하게 반응하게된다. 객체지향 적용하기상속과 조합상속보다는 조합을 사용하자상속은 시멘트처럼 굳어진 구조다. (부모와 자식의 결합도가 높다.)조합과 인터페이스를 활용하는 것이 유연한 구조다.상속을 통한 코드의 중복 제거가 주는 이점보다, 중복이 생기더라도 유연한 구조 설계가 주는 이점이 더 크다.상속 : extends → 부모 - 자식 간의 필수 오버라이딩 제약이 없고, 부모의 구현을 자식이 알고있는 관계 → 자식.부모메서드() 로 호출이 가능하여 재사용성이 향상됐다고 볼 수 있지만 객체 지향 관점에서는 캡슐화가 깨진 상태라고도 볼 수 있음조합 : 부모에는 공통 스팩 제공하는 인터페이스를 사용하고, 해당 부모의 구현체들은 부모의 스팩만 유지한 상태라면 부모에 독립적이고 변경이 많아질 수 있는 부분은 부모 클래스에 두지 않고 별도의 클래스로 분리함으로써 캡슐화와 확장성을 지킨다.주입 : 외부에서 부모 클래스나 자식 클래스가 의존하는 객체들을 주입함으로써, 객체 간의 강한 결합을 방지하고, 필요 시 구현체를 쉽게 교체할 수 있게 하여 유지보수와 테스트의 용이성을 높인다.Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체 값으로 취급하기 위해서, 불변성, 동등성, 유효성 검증 등을 보장해야 한다.엔티티와 vo의 차이(식별자 유무 차이)일급 컬렉션일급시민이란? -> 다른 요소(누군가의 파라미터, 변수, 함수의 결과로 return)에게 사용 가능한 모든 연산을 지원하는 요소일급컬렉션이란?컬렉션을 포장하면서 필드로 무조건 해당 컬렉션 하나만을 가지고 있는 객체컬렉션을 다른 객체와 동등한 레벨로 다루기 위함getter로 컬렉션을 반환할 일이 생긴다면 필드에서 가지고 있는 컬렉션과 같은 참조를 리턴하는 일이 생기지 않도록 new해서 새로운 컬렉션을 반환하자.enum의 특성과 활용상수의 집합변경이 정말 잦은 개념이면 enum보다 db로 관리하는게 좋을 수 있음.다형성 활용하기변하는 것과 변하지 않는 것을 분리하여 추상화하는 것.변하지 않는 것을 지켜야[추상] → 변경에는 닫혀있는 상태를 유지 가능변화하는 것에는 유동적으로 해결이 되어야[구체] → 확장에 열려있는 상태가 된다. 숨겨져 있는 도메인 개념 도출하기완벽한 설계는 없다. 그 당시의 최선이 있을 뿐.도메인 지식은 만드는 것이 아니라 발견하는 것설계할 때는 근시적, 거시적 관점에서 최대한 미래를 예측하려는 시도가 필요하다시간이 지나 만약 틀렸다는 것을 인지하면 언제든 돌아올 수 있도록 코드를 만들어야 한다.미션-> 주어진 코드에 대한 리팩토링얼리리턴추상화레벨 맞추기처리해야할 예외와 그렇지 않은 예외의 분리변수명, 함수명책임의 분리등.. 배운 지식을 활용하여 최적화하는 연습을 해보았다.   1주차 회고Keep (만족했고, 앞으로도 지속하고 싶은 부분)현재 코드를 작성하면서 "조상"과 "후손"이라는 표현을 사용한 점이 인상 깊었다. 이 표현은 코드의 지속성과 유지보수의 중요성을 잘 드러낸다고 생각이 들었다. 실무에서는 생산성과 클린 코드를 상반된 개념으로 보는 경우가 많은데, 이는 조직의 코드 문화에 긍정적이지 않다는 생각도 같이 들곤 했다. 이러한 문화를 개선하기 위한 방법론을 이번 학습을 통해 구체적으로 알게 되어 만족스러웠다..Problem (아쉬웠던 점)처음 보는 코드에 대한 빠른 이해가 부족하다는 점을 느꼈다. 즉, 현재 짜여진 코드를 너무 쉽게 납득해버리는 경향이 있는게 아닌가 싶다. 강사님처럼 코드를 보고 잠재적인 문제와 개선 가능성을 구조적으로 접근하는 시야가 필요하다고 생각한다. 이를 통해 "보는 눈"에 대한 중요성을 느꼈다.Try (다음에 시도해볼 점)구체적인 코드 설계에서 추상과 구체를 명확하게 분리하는 연습을 해보고 싶다. 처음부터 완벽한 설계를 할 수는 없겠지만, 도메인의 책임을 분리하고 리팩토링을 고려한 설계를 시도해봐야겠다. 우선 흐름대로 코드를 작성한 후, 리팩토링을 통해 개선하는 과정을 반복하며 사고력을 기르는 과정을 연습해보아야 할 거 같다.

백엔드워밍업클럽회고자기계발몰입하는개발자BE백엔드

구르밍

인프런 워밍업 클럽 BE 1기 - 2주차 발자국

UserController의 의아한점static이 아닌 코드를 사용하려면 인스턴스화 (new)가 필요하다UserController를 인스턴스화하고 있지 않은데 누가 하고 있는것인가???UserController는 JdbcTemplate에 의존하고 있다. 그런데 JdbcTemplate란 클래스도 설정해준적이 없다..! 어떻게 가져온거지?⇒ @RestController가 다해주고 있었다!!!!!⇒ UserController클래스를 API의 진입지점으로 만들 뿐 아니라 UserController 클래스를 스프링 빈으로 등록 시킨다. 스프링 빈(Bean)이란?서버가 시작되면, 스프링 서버 내부에 거대한 컨테이너를 만들게 되고, 컨테이너 안에는 클래스가 들어가게 된다.!이때 다양한 정보도 함께 들어있고, 인스턴스화도 이루어진다.JdbcTemplate도 스프링 빈에 등로되어 있었기에 사용할 수 있었다. (dependencies로 의존)UserRepository는 JdbcTemplate을 가져오지 못할까?JdbcTemplate을 가져오려면 UserRepository가 스프링 빈이어야 하는데 UserRepository는 스프링 빈이 아니다!!→ UserRepository를 스프링 빈으로 등록하자!스프링 컨테이너를 왜 사용할까?메모리에 저장하는 레포를 만든다고 가정하고 코딩…→ MySQL로 저장하고 싶은데?→ 관련된 모든 것들을 다 MySQL로 변경하는 레포로 변경해야함!!! 데이터를 메모리에 저장할지, MySQL에 저장할지 Repository의 역할에 관련된 것만 바꾸고 싶은데 BookService까지 바꿔야 한다. (OMG)⇒ Java의 interface를 활용하자!그래도 Service를 수정해야하는 상황이 발생.. 스프링 컨테이너를 사용하면?BookMemoryRepository, BookMySqlRepository 둘 중 BookService에 쓰일 것을 컨테이너가 선택한다!!→ 이런 방식을 제어의 역전(IoC, Inversion of Control)이라 한다.→ 컨테이너가 선택해 BookService에 넣어주는 과정을 의존성 주입(DI, Dependency Injection)라고 한다.@Primary를 붙여주면 해당 어노테이션이 있는 곳이 사용된다. (우선권 결정)⇒ 나중에 바꿔야 Memory에서 mysql로 바꾸는 작업이 있어서 코드를 다 바꿔야 할 경우 @Primary를 붙여 스프링이 자동으로 해당 레포로 선택하게 하면 된다! 스프링 컨테이너를 다루는 방법빈을 등록하는 방법@Configuration클래스에 붙이는 어노테이션@Bean을 사용할 때 함께 사용해 주어야 한다.@Bean메소드에 붙이는 어노테이션메소드에서 반환되는 객체를 스프링 빈에 등록한다.UserRepository.java에 있는 @Repository 어노테이션 삭제 후 @Bean으로 등록언제 @Service, @Repository를 사용해야하나??→ 개발자가 직접 만든 클래스를 스프링 빈으로 사용할 때@Configuration, @Bean는 언제 사용?→ 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때@Component주어진 클래스를 ‘컴포넌트’로 간주한다.이 클래스들은 스프링 서버가 뜰 때 자동으로 감지된다.→ 사실 숨겨져 있었다.. 타고 들어가면 사용되고 있는것을 확인할 수 있다.→ 컨트롤러, 서비스, 리포지토리가 모두 아니고, 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용되기도 한다. 스프링 빈을 주입 받는 몇 가지 방법생성자를 이용해 주입받는 방식 (가장 권장)기존에는 @Autowired 어노테이션을 붙여야했는데 스프링 버전이 업데이트 되면서 안붙여도 자동으로 등록되게 되었다.setter와 @Autowired 사용 → setter를 사용할 경우 오류발생 확률 높아짐필드에 직접 @Autowired 사용 → 테스트하기 어렵다.@Qualifier: 여러개의 후보군이 있을때 그 중 하나를 특정해서 가져올 수 있게 끔 한다.스프링 빈을 사용하는 쪽, 스프링 빈을 등록하는 쪽 모두 @Qualifier를 사용할 수 있다.스프링 빈을 사용하는 쪽에서만 쓰며, 빈의 이름을 적어주어야 한다.양쪽 모두 사용하면, @Qualifier끼리 연결된다.@Primary vs @Qualifier → 동시에 사용할 경우 어떤게 우선일까?사용하는 쪽에서 직접 적어준 @Qualifier가 이긴다.SQL을 직접 작성하게되면..SQL을 직접 작성할 경우 오류가 나도 확인하기 힘들다⇒ 컴파일 시점에 발견되지 않고 런타임 시점(서버가 이미 가동된 후)에 발견된다특정 데이터베이스에 종속적이게 된다.⇒ 다른 DB를 사용할경우 다 바꿔줘야 한다.반복 작업이 많아진다. 테이블을 하나 만들 대마다 CRUD쿼리가 항상 필요하다.데이터베이스의 테이블과 객체는 패러다임이 다르다.→ JPA( Java Persistence API) 등장!: 객체와 관계형 DB의 테이블을 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java 진영의 규칙 유저테이블에 대응되는 Entity Class 만들기@Entity 어노테이션을 User 클래스에 붙인다.→ @Entity : 스프링이 User객체와 user 테이블을 같은 것으로 바라본다. (저장되고, 관리되어야 하는 데이터)우선 테이블의 PK인 id를 만들어보자@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null;@Id : 이 필드를 primary key로 간주한다.@GeneratedValue : primary key는 자동 생성되는 값이다.(strategy = GenerationType.IDENTITY)여기 부분은 DB종류마다 자동생성 전략이 다르다!! 주의!!MySQL의 auto_increment를 사용했기에 IDENTITY를 매칭@Entity를 사용할 경우 - 매개변수가 하나도 없는 기본 생성자가 꼭 필요하기 때문에 다음 코드를 작성!protected User() {}@Column : 객체의 필드와 Table의 필드를 매핑한다.→ null이 들어갈 수 있는지 여부, 길이 제한, DB에서의 column이름 등등…@Column(nullable = false, length = 20, name = "name") //name varchar(20) private String name;null이 들어갈 수 없거, 길이는 20자, 필드의 이름은 name (이 경우는 name = “name” 동일하기에 생략 가능) @Column 은 생략가능하기 때문에 널이 들어갈 수 있고 굳이 특정할필요 없다면 생략이 가능하다. JPA 설정 추가 - application.ymljpa: hibernate: ddl-auto: none properties: hibernate: show_sql: true format_sql : true dialect: org.hibernate.dialect.MySQL8Dialectddl-auto : 스프링이 시작할 때 DB에 있는 테이블을 어떻게 처리할지none 외에 다른 종류create : 기존 테이블이 있다면 삭제 후 다시 생성create-drop : 스프링이 종료될 때 테이블을 모두 제거 -… 사용 조심할것 ! 데이터가 모두 날라갈 수있음.update : 객체와 테이블이 다른 부분만 변경validate : 객체와 테이블이 동일한지 확인none : 별다른 조치를 하지 않는다.show_sql : JPA를 사용해 DB에 SQL을 날릴 때 SQL을 보여줄 것인가format_sql : SQL을 보여줄 때 예쁘게 포맷팅 할 것인가dialect : 방언, 사투리.. → 이 옵션으로 DB를 특정하면 조금씩 다른 SQL을 수정해준다. Spring Date JPA를 이용하여 자동으로 쿼리 날리기기존 UserRepository.java를 UserJdbcRepository.java로 변경하고domain > user > UserRepository 인터페이스를 생성하자생성 후 JpaRespository를 상속 받는다.public interface UserRepository extends JpaRepository<User, Long> {}<User, Long> → User의 primary key인 Id의 타입이 Long이라 Long으로 작성한다. 유저 데이터 정보 추가하기 (INSERT)public void saveUser(UserCreateRequest request) { userRepository.save(new User(request.getName(), request.getAge())); }JpaRepository를 상속 받는 userRepository에 save메소드를 객체에 넣어주면 INSERT SQL이 자동으로 날아간다. → save되고 난 후의 User는 id가 들어 있다유저 정보 조회하기 (SELECT)public List<UserResponse> getUserList(){ return userRepository.findAll().stream() .map(user -> new UserResponse(user.getId(), user.getName(), user.getAge())) .collect(Collectors.toList()); }findAll() : 해당 테이블의 모든 데이터를 조회한다.user를 stream으로 mapping시켜주고 user에 new UserResponse로 id, name, age를 넣어준다.그 후 결과를 List로 반환유저 정보 조회 후 수정하기 (UPDATE) public void updateUser(UserUpdateRequest request) { User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); userRepository.save(user); }findById를 사용하면 id를 기준으로 1개의 데이터를 가져온다..orElseThrow(IllegalArgumentException::new);→ Optional의 orElseThrow를 사용해 User가 없다면 예외를 던진다. 있을경우 user에 담긴다user.updateName(request.getName()); userRepository.save(user);도메인 user의 updateName메소드에 변경할 reqest.getName을 인자로 넣어 이름을 변경해주고save를 통해 user의 정보를 저장한다. (자동으로 UPDATE SQL이 날라가게 된다.) 어떻게 SQL을 작성하지 않아도 동작하지? JPA인가?→ Spring Data JPA가 도와준다.: 복잡한 JPA 코드를 스프링과 함께 쉽게 사용할 수 있도록 도와주는 라이브러리 By앞에 들어갈 수 있는 구절 정리find : 1건을 가져온다. 반환타입은 객체가 될수도 있고, Optional<타입>이 될 수도 있다.finalAll : 쿼리의 결과물이 N개인 경우 사용. List<타입> 반환exists : 쿼리 결과가 존재하는지 확인. 반환 타입은 booleancount : SQL의 결과 개수 반환타입은 longBy뒤에 들어갈 수 있는 구절 정리GreaterThan : 초과GreaterThanEqual : 이상LessThan : 미만LessThanEqual : 이하Between : 사이에SELECT * FROM user WHERE age BETWEEN ? AND ?;List<User> findAllByAgeBetween(int startAge, int endAge);StartsWith : ~로 시작하는EndsWith : ~로 끝나는트랜잭션 이란 : 쪼갤 수 없는 업무 최소 단위→ 모든 SQL을 성공시키거나 하나라도 실패하면 모두 실패시키자!트랜잭션 시작하기start transaction;트랜잭션 정상 종료하기 (SQL 반영)commit;트랜잭션 실패처리(SQL 미반영)rollback; 트랜잭션 적용과 영속성 컨텍스트우리가 원하는 것은서비스 메소드가 시작할 때 트랜잭션이 시작되어서비스 메소드 로직이 모두 정상적으로 성공하면 commit 되고서비스 메소드 로직 실행 도중 문제가 생기면 rollback 되는 것@Transactional 어노테이션으로 우리가 원하는 것을 할 수 있다!SELECT 쿼리만 사용할경우, readOnly 옵션을 사용하여 데이터 변경을 위한 기능이 빠져 약간의 성능 향상이 있다. 영속성 컨텍스트란?테이블과 매핑된 Entity 객체를 관리/보관하는 역할⇒ 스피링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨나고, 트랜잭션이 종료되면 영속성 컨텍스트가 종료된다.변경 감지 (Dirty Check): 영속성 컨텐스트 안에서 불러와진 Entity는 명시적으로 save하지 않더라고, 변경을 감지해 자동으로 저장된다.@Transactional public void updateUser(UserUpdateRequest request) { User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); user.updateName(request.getName()); //userRepository.save(user); }유저의 정보가 업데이트가 되었네? 바뀌었구나? 하고 자동으로 저장된다. 따라서 save메소드를 작성해주지 않아도 된다.쓰기 지연: DB의 INSERT / UPDATE / DELETE SQL을 바로 날리는 것이 아니라, 트랜잭션이 commit될 때 모아서 한 번만 날린다.예시로 유저가 저장되는 부분이 여러개 일때, 하나씩 날려서 저장하는것이 아니라 우선 기억해두고 한번에 저장하여 DB에 보내게 된다. 1차 캐싱: ID를 기준으로 Entity를 기억한다.쓰기 지연과 비슷하게 이전에 캐싱된 객체를 기억하고 있다가 동일한 값이면 DB에 계속해서 요청하지 않고 알려준다.책 생성 API 개발하기create table book (id bigint auto_increment, name varchar(255), primary key(id));name varchar(255)을 사용한 이유?@Column의 length 기본값이 255문자열 필드는 최적화를 해야 하는 경우가 아닐 때 조금 여유롭게 설정하는 것이 좋다.회고4번째과제는 과일가게 판매에 관련된 내용이었다. 삼단분리 후 뭔가 직접 구현해봐야하는 문제라 그런지 사실 과제를 하면서 재밌게 느껴졌다. 단순히 요구사항에 나와있는데로만 작성하다보니 id를 왜 long 타입으로 써야했을까? 등 '왜' 라는 생각을 잘 하지 못했는데 앞으로는 왜? 의문점을 가지고 생각해봐야겠다!!5번째 과제에선 클린코드에 나와있는 내용을 바탕으로 더 좋은 코드로 변경해보는 과제이었다.문제를 보고 우선 각각의 기능을 하는 함수로 나누어 분리해야겠다는 생각이 들었다. 또 if-else문을 지양해야한다고 했지만, 너무 많은 조건을 가지고 있는 if-else문의 경우에는 사용을 하는것이 오히려 나을수도 있다는 블로그 글을 참고하여조건부분을 따로 함수로 만들어 그 함수를 호출하는 if-else문을 생각하고 1차 리팩토링 하였다.하지만 그래도 if-else문에는 너무 반복되는 부분이 많았고 이부분을 결국 for문으로 변경하였다..!금요일에 깜짝 라이브를 참석하지 못해서 너무 아쉬웠지만 따로 내용을 올려주셔서 참고할 수 있었다 : )

백엔드워밍업BESpring

긱북이

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

첫 걸음인프런 워밍업 클럽 1기 BE에서 자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] 강의를 수강하며 작성하였다.강의 학습HTTP와 APIHTTP란 다른 컴퓨터로 데이터를 보내기 위한 데이터 표준이다.API는 정해진 약속에 따라 특정 기능을 수행하는 코드이다. HTTP Method 중 하나인 GET은 쿼리를 통해 정보를 보낸 후 데이터를 요청한다.→ /add?number1=10&number2=20 Controller는 API가 실행되는 입구와 같다.@GetMapping("/add") public int addTwoNumbers(@RequestParam int number1, @RequestParam int number2) { return number1 + number2; } GET 방식에서 매개변수를 각각 하나씩 입력받을 때에는 @RequestParam을 사용하여 변수에 쿼리의 값을 넣는다.만약 매개변수가 많을 경우, DTO 객체로 만들어 관리한다.@Getter public class CalculatorAddRequest { private final int number1; private final int number2; public CalculatorAddRequest(int number1, int number2) { this.number1 = number1; this.number2 = number2; } }GET 방식의 DTO 객체는 반드시 생성자를 포함해야 한다. DTO 객체를 이용한 Controller는 다음과 같다.@GetMapping("/add") public int addTwoNumbers(CalculatorAddRequest request) { return request.getNumber1() + request.getNumber2(); }DTO를 이용할 때는 @RequestParam을 제거해야 하며 변수를 객체로 감싸 불러오기 때문에 getter를 사용해야 한다. 마찬가지로 HTTP Method인 POST는 Body를 통해 정보를 보낸다.객체 표기법인 JSON을 사용하고, List를 사용하거나 JSON 안에 JSON을 사용하는 것도 가능하다.POST 방식에서 DTO를 이용한 Controller는 다음과 같다.@PostMapping("/multiply") public int multiplyTwoNumbers(@RequestBody CalculatorMultiplyRequest request) { return request.getNumber1() * request.getNumber2(); } @RequestBody를 사용하여 HTTP Body로 들어오는 JSON을 DTO 객체 형태로 변환한다.GET 요청의 @RequestParam과는 달리 DTO 객체를 사용해도 Annotaion을 생략할 수 없다. GET 요청과 달리 POST 요청은 생성자가 필요하지 않다. Domain(Entity)와 DTO의 차이DTO는 계층 간 데이터 교환을 위해 사용된다. 반면, Domain은 DB 테이블과 매핑되어 데이터를 저장하거나 관리하는 실제 비즈니스 도메인을 표현한다. DTO는 단순히 어떤 데이터로 통신할 것인지 정의하고, 상세한 정의는 Domain에서 한다. Spring에서 Database 사용하기private final JdbcTemplate jdbcTemplate; public UserController(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @PostMapping("/user") public void saveUser(@RequestBody UserCreateRequest request) { String sql = "insert into user (name, age) values (?, ?)"; jdbcTemplate.update(sql, request.getName(), request.getAge()); }JdbcTemplate를 이용하면 Spring에 SQL을 전달할 수 있다.SQL문을 작성하여 문자열 변수로 저장하는데, 값이 들어갈 부분에 ?를 사용하면 데이터를 넣을 수 있다.  과제 수행1일차 과제 : JAVA Annotation어노테이션은 실제 데이터가 아닌 메타데이터로써 클래스와 메서드에 추가하여 다양한 기능을 부여한다.반복적인 코드 작성을 줄일 수 있어 코드량이 감소하고, 코드의 역할을 명확히 지정할 수 있어 유지보수가 용이하다는 장점이 있다.사용자가 원하는 기능을 수행하는 커스텀 어노테이션을 만드는 방법도 알아보았다. 2일차 과제 : GET & POST API날짜를 받는 API를 구현할 때, String으로 날짜를 받은 후 LocalDate로 변환하는 과정을 거쳤다.그러나, 스프링 부트 2버전에서는 @DateTimeFormat을 사용해 LocalDate를 바로 받을 수 있다고 한다.3버전대에서는 어노테이션 없이 LocalDate를 바로 받을 수 있다. 3일차 과제 : 람다식과 익명 클래스익명 클래스란 이미 정의되어 있는 부모 클래스의 자원을 일회성으로 재정의한 클래스이며, 자식 클래스를 정의할 필요 없이 객체화가 가능하다.람다란 자바의 인터페이스를 익명 클래스로 구현한 익명 구현 객체를 짧게 표현한 것이다. 메서드 타입과 이름, 매개변수 타입, 중괄호, return문을 생략하고 화살표 기호를 넣는다. 4일차 과제 : 과일 가게 API 만들기enum 타입을 통해 과일의 판매 여부를 ‘SOLD’, ‘NOT_SOLD’의 두 가지 상태로 구분하였다.COALESCE()를 사용하여 팔린 과일 혹은 팔리지 않은 과일이 존재하지 않을 경우, NULL이 아닌 0으로 표시되도록 하였다.쿼리의 결과가 하나이므로 queryForObject를 이용해 SQL의 결과를 직접 long 타입의 객체로 매핑하였다.  느낀 점Spring의 기초를 복습하면서 ‘이건 왜 이렇게 쓰일까’와 같은 질문을 던지며 천천히 학습할 수 있었다.특히, 인프런의 각 강의에 있는 질문 게시판을 활용하였다.강의를 들으며 의문이 든 부분들은 이미 선배 러너들이 질문을 했고, 강의자 분께서 자세하게 답변을 달아 주셨기 때문에 편하게 학습할 수 있었다. 매일 과제가 있지만 하루에 학습할 양을 안내해 주는 진도표가 존재하고, 하루치 공부 양이 그렇게 많지 않았기 때문에 오히려 꾸준하게 하루 공부를 진행할 수 있어 좋았다.일주일을 돌아보며 작성하는 발자국은 그동안 배운 내용을 정리하고, 느낀 점을 회고할 수 있어 좋은 방법인 것 같다.강의를 들으며 개조식으로 정리한 내용을 글로 풀어 쓰니 내가 얼만큼 이해하고 있는지 확인할 수도 있다.

백엔드워밍업클럽BE발자국1주차

김찬희

[인프런 워밍업 클럽 스터디 1기] BE 2주차 <두 번째 회고록>

스프링 컨테이너 (1) Static 이 아닌 코드를 사용하려면 인스턴스화가 필요하다 . 이때, @RestController( Controller 클래스) 등을 사용해 클래스를 스프링 빈으로 등록시켜준다. 서버가 시작되면 스프링 서버 내부에 컨테이너를 만들게 되는데 이때, 스프링 컨테이너에 기존에 등록되어 있던 빈들이 다 등록 되고 나면 @RestController 등으로 스프링 빈 등록한 클래스 들이 등록된다. 이때, 다양한 정보들도 함께 들어있고 인스턴스화도 이뤄진다. ex ) UserController에 @RestController를 붙였다면 이름 : UserController , 타입 : UserController 로 등록되는 것이다. 이렇게 스프링 컨테이너 안에 들어간 클래스들을 스프링 빈이라고 한다. 우리가 이전에 사용했던 JdbcTemplate 은 이미 스프링 빈으로 등록되어 있어 따로 등록하지 않아도 된다. ( 우리가 bulid.gradle 에서 dependencies 설정을 해줬기 때문이다.) 이후에 빈 등록이 완료되면 필요한 의존성을 자동으로 연결시켜주는데 UserController는 JdbcTemplate의 의존 관계가 연결된다. 그런데 만약 이때 BookController -> BookService-> BookMemoryRepository 에서 BookMySqlRepository를 연결하고 싶다면 기존의 Repository 코드뿐만 아니라 Service 코드에서의 인스턴스화 부분등 수정코드가 발생한다 우리는 이를 해결하기 위해 스프링 컨테이너를 사용해서 BookController -> BookService -> <interface>BookRepository <- BookMySqlRepository <- BookMemoryRepository 로 BookRepository 라는 인터페이스를 2개의 구현체가 구현하는 형태를 만들고 컨테이너가 어떤 Repository를 사용할지 선택하게 한다. 이러한 방식을 제어의 역전 (IOC ,Inversion of Control) 이라고 하고 컨테이너가 구현체를 선택해 BookService에 넣어주는 과정을 의존성 주입 (DI, Dependency Injection)이라고 한다.(2) Bean 을 등록하는 방법 @ Configuration : 클래스에 붙이는 어노테이션 , @Bean 과 함께 사용한다.@ Bean : 메소드에 붙이는 어노테이션 , 메소드에서 반환되는 객체를 스프링 빈으로 등록한다.@Service, @Repository : 개발자가 직접 만든 클래스를 스프링 빈으로 등록시 사용 @Component : 주어진 클래스를 컴포턴트로 간주한다. 이 클래스들은 스프링 서버가 시작할때 자동으로 감지(스프링 빈 등록) , Component는 @Service ,@ Controller, @Repository 에 다 있다.(3) 스프링 빈을 주입 받는 방법생성자 주입 ( 권장 ) : 프로젝트에서 제일 많이 사용하는 방식으로 private final 타입 변수 ; 로 선언후 생성자를 통해 인스턴스화 하는 방법 Setter 와 @Autowired 사용 : 누군가 Setter 사용시 오류가 발생할 여지가 있음 (주의) 생성자 주입에도 @Autowired 를 사용해야 하지만 생략이 가능하다.3. 필드에 직접 @Autowired 사용 : 제일 편리하지만 추후 테스트 작성시 코드 작성을 어렵게 하는 요인이 된다.  (4) <interface> 의 구현체가 여러개 일때 선택 기준  1. @Primary : @Primary 어노테이션이 있는 구현체가 우선권이 있다. 2.@Qualifier : 사용하는 Service 와 사용하는 Repository 구현체에 @Qualifier("main")을 적어주고 사용하는 Service 에 @ Qualifier("구현체이름") 을 추가한다.이때 @Primary 와 @Qualifier을 동시에 사용할 경우 더 자세한 @Qualifier 이 우선 순위를 가진다.회고 이번 주는 강의를 복습 하고 개념을 정리하는데 집중했다. 강의를 2번씩 들어보면서 스프링 컨테이너 ,빈 과 등록방법, DI, IOC 등 스프링의 핵심이라고 할 수 있는 개념을 정말 이해해보는데 시간을 많이 사용했고 전보다 깊이 있는 이해가 되었다. 다음주 강의에서는 개발한 API를 배포하는 과정을 거치는데 기대된다. 나만의 서비스를 만들어보고 싶은 마음이 컸는데 강의를 통해 목표에 조금 더 가까워진듯하다.

백엔드BE

mingle

[인프런 워밍업 클럽 스터디 BE 1기] 첫 번째 발자국

늦었지만 올려보는 첫 번째 발자국참여 계기취업 한지 벌써 2년이 되어간다. 일을 할수록 부족한 나를 마주하는 순간이 많았다. 그래서 항상 공부하겠다고 다짐했지만, 퇴근 후 공부는 생각보다 쉽지 않았고…. ㅎㅎ.. 그런 와중에 인프런에 들어왔다가 워밍업 클럽 스터디 배너를 보고 관심이 갔다. BE 강의 커리큘럼을 보니 회사에서 사용하지 않는 기술도 있어 더 흥미가 갔고 공부하기 좋은 기회라고 생각해서 지원하게 됐다. 배운 내용환경 세팅, HTTPHTTP Method(GET, POST, PUT, DELETE)를 활용한 간단한 API 개발MySQL과 DDL, DMLSpring과 DB 연결. CRUD API 개발.관심사의 분리 가장 인상 깊은 강의는 리팩토링이다. 회사 일을 하다가 내가 짠 코드에 대해 다른 직원분으로부터 문의를 받은 적이 있다. 시간에 쫓겨 작성한 부분이었고.. 강의를 들으면서 매우 많이 찔렸다. 아무리 바빠도 이후에 내 코드를 읽을 동료, 나를 위한 개발을 해야겠다고 생각했다. 화이팅~스터디에서 가장 만족하는 점은 과제다. 과제를 하다 보면 관련된 다른 궁금한 점이 생기고, 계속 샛길로 빠지게 된다. 한 과제를 하는데 하루 종일 걸린다ㅎㅎ.. 기한이 2일이라서 다행이다. 과제 덕분에 더 많이 배울 수 있었고 재밌었다. 이번 주에 배울 내용과 할 과제도 기대가 된다. 과제

백엔드BE워밍업1기

강예인

[인프런 워밍업 클럽_1기 BE] 첫번째 발자국

강의 수강일주일 동안 학습했던 내용IntelliJ설치, Spring 환경 설정, MySQL 설치개발을 위한 기본적인 환경설정을 해주었다. 네트워크 기초API 통신을 위한 기본적인 API동작 방식에 대해 학습했다.데이터베이스데이터베이스 세팅과 CRUD에 대해 학습했다.API 제작GET,POST,PUT,DELETE를 써서 사용자를 등록하고 수정하고 삭제하고 조회했다.사용자 정보가 없을 경우에 대해 예외처리를 했다.클린 코드클린코드가 왜 중요한지에 대해 학습했다.Controller가 하나의 기능만 하도록 분리하는 작업을 했다.미션1. 어노테이션어노테이션이 무엇인지와 장단점에 대해 알아보고 나만의 어노테이션을 만들어보았다.미션2. API 실습두 수의 합,차,곱을 반환하는 API제작LocalDate를 활용하여 날짜 요청시 요일 반환 API제작배열 안의 숫자를 모두 더하는 API 제작미션3. 람다식자바에서 람다식이 왜 생겨났는지와 람다식 문법을 알아보았다.회고참가 계기내가 주로 쓰고 있는 언어는 javascript와 C#인데 한국에서는 java를 주로 써서 블로그를 봐도 java에 대한 내용이 더 많이 보이기도 했고 코딩 테스트에서나 개발자를 채용할 때도 대부분 java가 기준이였다. 그래서 나도 한 번 java를 사용해서 서버를 만들어볼까 생각했었고 마침 인프런 워밍업 클럽 1기 스터디 모집 글을 보게 됐다. java를 안해본지 좀 오래됐는데 마지막으로 java를 쓴지는 한...3년 전이였나 기억도 나질 않는다. 그래도 기초부터 시작하니 커리큘럼대로 따라가기만 하면 문제 없을 것 같아서 신청하게 되었다.새로운 것IntelliJ  내가 처음에 java를 배웠을 때 Eclipse를 썼는데 이젠 IntelliJ를 쓰나보다. 솔직히 java는 내 관심 영역은 아니었기에 현재 쓰고 있는 컴퓨터엔 전혀 세팅이 되어 있지도 않았다. 내가 친숙한건 vscode,Visual Studio 이것 뿐.. Spring boot도 처음 써본다... mysql?  mysql은 써보긴했는데 이것도 대학생 때 썼으니 오래 전이긴하다. Oracle이랑 비슷해서 문제는 없긴한데 bigint 타입이 있어서 이건 뭐지? 했는데 ORACLE에서 NUMBER(19)를 표현한 거였다. 조금씩 다른데 적다가 헷갈리긴한다. 강의에서 IntelliJ에 DB연결해서 사용하는 부분이 있었는데 유료라서 굳이 돈을 써야할까 싶어서 CLI 창으로 했다. 미션미션 1. 어노테이션어노테이션이 무엇인지와 나만의 어노테이션을 만들어보는 미션이 있었는데 어노테이션이라는 개념을 처음 들어봤다. 그래서 찾아봤는데 C#에서 어튜리뷰트와 좀 비슷한 것 같았다. 이번에 flutter도 시작했는데 dart에도 어노테이션을 지원한다는 것을 알게됐다. java에서 annotation을 만들어보면서 다른 언어에는 어떻게 작동하는지 비교하면서 해결했다.미션2. API 실습  문제2번에서 C#에서 DateTime이 java에서 localDate를 쓰는 것을 확인했다. 작동 방식은 비슷한 것 같았다. 처음에 요청 받을 때 LocalDate로 받는 방식을 썼었다. 에러가 나서 설마 LocalDate로 받아와서 그런가 싶어서 String으로 바꿨었는데 결국 오타 때문에 안된 거였었다. 근데 바꾸기 귀찮아서 다시 안바꿨다. 과제가 끝나고 멘토님이 스프링 부트 2.x.x 버전대에서는 @DateTimeFormat 이라는 어노테이션을 사용해서 LocalDate 를 바로 받을 수 있고 3.x.x 버전 대에서는 어노테이션 추가 없이 LocalDate를 받을 수 있다고 답변 달아주셔서 String으로 받은게 조금 찔려서 다시 수정했다.미션3. 람다식자바에서 람다식에 대한 과제를 내주셨을 때 C#과 javascript,dart에서 쓰고있어서 반가웠다. C# LINQ와 비슷한 스트림(Stream) API도 알게 되었다. 각 언어별로 람다식으로 예제를 만들어서 연습해봤다.느낀점이번주 동안 강의를 듣고 미션을 수행하면서 각 언어 별로 어떻게 다른지 비교하는 방식을 통해 오히려 java에 대한 내용 뿐만 아니라 다른 언어를 이해하는데 도움이 된 것 같다. 언어 별로 독특한 기능과 차이점을 보면서 언어마다 어떤 문제를 해결하기위해 도입했는지 스토리를 찾아보는게 재밌었다.이번주 동안 진도표와 과제제출을 성실하게 수행한 나한테 칭찬한다..ㅎㅎ진도표와 과제제출을 통해 나태한 나에게 압박감을 주는 스터디가 나한테 맞는 것 같다.다음주에도 열심히 따라가야지!!

백엔드발자국인프런워밍업클럽1기BE

김찬희

[인프런 워밍업 클럽 스터디] 1기 첫번째 발자국

회고록 독학으로 공부를 하다보니 자기 객관화와 지금까지 뭔가 프로젝트를 처음부터 끝까지 완성해본 경험이 없고 부족하다는 생각이 들어서 무작정 신청하게 되었다. 처음에는 아 그냥 간단한 게시판 구현해보고 이후에 내가 기능 몇가지 추가해서 프로젝트 구현 하면 되겠다 정도로 가볍게 생각하고 들었던 강의였는데 강의를 하나씩 듣다보니 이전에 학습했던 내용들이 조립되는 느낌이 들어서 정말 개발에 흥미를 붙일 수 있었던 시간이었다. 사실 spring에 대해서 학습하다보면 전체적인 흐름을 놓치게 될때가 종종 있는거 같다. 스프링의 역사가 긴 만큼 하나의 기술에 대해 깊이 있게 파다보면 주변의 다른 기술과의 연결성을 잃을수 있다고 생각하는데 이 강의에서는 그 연결성에 대한 부분을 학습할 수 있어서 성장하는데 도움이 되었다.라이브러리 VS 프레임워크  라이브러리 : 프로그래밍을 개발할 때 만들어져 있는 기능을 가져다 사용하는 것 프레임워크 : 프로그래밍을 개발할 때 미리 만들어져 있는 구조에 코드를 가져다 끼워 넣는 것이때 강의에서는 요리에 비유를 했는데 김치 찌개를 만들때 라이브러리는 김치를 마트에서 가져오는 것, 프레임워크는 김치찌개 원데이 클래스에가서 만드는 것 이라는 설명이 이해하기 쉽게 와닿았다.@Annotation@SpringBootApplication : 스프링을 실행시키기 위해 필요한 다양한 설정들을 모두 자동으로 해주는 Annotation이다.@Annotation에 대한 추가 학습을 통해 개인블로그에 정리해보았다.https://velog.io/@chanheekim125/Annotation 네트워크란?네트워크 : 인터넷 주소 체계 ( 무언가(정보)를 전달할 때 필요한 체계)IP : 컴퓨터별로 갖는 고유 주소 ( ex) 244.69.51.9 ) 처럼 표시한다.DNS : 우리가 사이트에 접속할때 244.54.21.2 를 기억하고 입력할 수는 없으니까 ex) spring.com 등 이름을 사용PORT: 각 IP에는 여러가지의 프로그램을 사용하는데 이때 PORT 번호로 구분한다.HTTP와 API HTTP : 컴퓨터 간의 통신 표준 방식 ( 택배에서 운송장과 같은 개념 )HTTP 요청은 HTTP Method로 GET,POST가 주로 사용되며 PUT,DELETE 등이 있다.HTTP 요청에서 데이터를 전달하기 위해 쿼리(GET) 와 바디(POST)를 사용한다.이때 클라이언트와 서버는 HTTP를 주고 받으며 기능을 동작하는데 이때 정해진 규칙을 API라고 한다.API 개발하기 GET API POST API 직접 API 명세를 읽어보고 개발 해보면서 request를 받아서 데이터를 어떻게 처리하는지 알아보았다.https://velog.io/@chanheekim125/HTTP-API-%EA%B0%9C%EB%B0%9C데이터 베이스 이전 Section 에서는 그동안 Memory에 데이터를 저장하고 있었기 때문에 서버를 내리면 모든 데이터가 없어지는 현상이 발생했었다. 우리는 이러한 현상을 방지하기 위해 데이터를 DB에 SQL 문법을 사용해서 저장,조회,삭제,수정 하는 방법에 대해 학습하였다.SQL 문법은 정처기,SQLD 시험을 준비하면서 익숙했기에 개념보다는 MYSQL 실행툴에 익숙해 지는데 시간을 쓴거 같다. 정리 지금까지는 정말 기초라고 할수 있는 내용에 대해 학습해 보았다. 기본적인 API 기능을 구현할 수 있게 되었고 또, 무작정 강의만 수강해서 진도를 나가는 것이 아니라 과제를 하면서 스스로 동작 순서나 구현방법을 생각해보는 시간에 가장 많이 성장하는 나를 볼 수 있었다.

백엔드인프런워밍클럽StudyBE

이동원

인프런 워밍업 스터디 클럽 0기 BE - 1주차 발자국

1주차 회고 인프런에서 처음으로 시작하는 스터디 클럽에 들어간지 1주일이 되었습니다. 강의는 최태현님의 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를 강사님께서 하루마다의 과제분을 주시고 공부한것을 블로깅 하는 방식이었습니다. 처음 과제의 양을 봤을때는 "할만한대?" 라는 생각으로 강의를 듣고 블로깅을 하였지만, 처음의 호기로운 생각은 어디가고 이것을 어떻게 글로써 풀어서 쓸지, 강의의 내용을 제대로 이해한 것은 맞는지 엄청나게 해매는 시간을 맛볼수 있었습니다. 강사님께서 훅훅 넘어가시고 중요한 개념을 짚어 주시고 진도를 나가시는데, 이것을 블로깅으로 조금 궁금한 점들을 계속해서 검색해보니, 아 spring이란것이 강사님께서 처음 접하는 사람들을 위해 최소한의 설명으로 이해하고 따라올 수 있도록 설명해주신 거구나, spring의 양은 방대하다는 것과 알아야 할 개념들이 많음을 피부로 느낄수 있었습니다. 또한 저의 부족함과 해야할 공부량에 압도 당하는 시간도 많았습니다. 스터디를 진행하면서 디스코드에 오늘의 과제분과 블로깅을 올려주시는 분들을 보면서 저와 비교되는 퀄리티와 이렇게 같은 강의를 듣고도 이렇게나 수준 차이가 난다고? 라는 생각을 떨쳐 버릴수 없었습니다. 그 와중에 강사님께서 해주신 말씀중 기억나는 것이 있었는데 한 수강생의 질문이 "백엔드 개발자로 취업하기 위해서 얼만큼 깊게 공부해야 하나요?" 라는 어조의 질문이었는데 "정해진 것은 없다 하지만 보통 아는것과 실력이 연봉과 비례한다" 라고 말씀해 주셨었습니다(잘 기억안남ㅎㅎ). 강사님의 말씀을 들으며 한가지 다짐을 했는데 '지금이 어떻든 나아지기 위해 지금의 스터디에 참가하고 좀더 잘하는 사람들의 결과물을 보며 내것으로 만들고 더 배우면 될것 아닌가?' 라는 다짐을 하게 되었으며 부족한 만큼 이 스터디 끝까지 완주해서 많은 것을 남겨 가야겠다 마음 먹었습니다. 이러한 스터디를 만들어 주신 인프런 분들께 감사드리고 열과 정성을 다해 가르쳐 주시는 강사님과, 함께 스터디를 참가하고 있으신 동료분들깨 감사드립니다. 이만 공부하러 가보겠습니다.

study워밍업BE

채널톡 아이콘