블로그
전체 11#카테고리
- 백엔드
- 프로그래밍 언어
#태그
- 워밍업클럽
- 백엔드
- 스터디
- 후기
- 스프링
- 개인학습
2024. 10. 27.
1
[인프런 워밍업 스터디 클럽 2기 백엔드] 4주차 + 회고
해당 글은 인프런 박우빈 강사님의 「Practical Testing: 실용적인 테스트 가이드」을 바탕으로 작성하였습니다. 강의 요약 Presentation Layer외부 세계의 요청을 가장 먼저 받는 계층사용자 입력 값, 프론트엔드에서 주는 값, 요청 등...주요 로직은 없지만, 최소한의 유효성 검증을 수행한다. 레이어드 아키텍쳐의 단점도메인에 강하게 의존도메인에 레포지토리를 위한 어노테이션을 필연적으로 붙여야 하는 문제 발생⇒ 대안: 헥사고날 아키텍쳐 Mockito로 stubbing 하기가짜 객체를 만들어 행위를 지정하고 테스트하는 방법 (자세한 내용은 미션 6 참조) Test Doubledummy: 아무것도 하지 않는 깡통 객체fake: 단순 형태로, 동일 기능을 수행. 프로덕션에서 쓰기에는 부족한 객체stub: 테스트에서 요청한 것에 대해 기록하여 보여줄 수 있는 객체. 정의하지 않은 요청에는 무응답spy: stub이면서, 호출된 내용을 기록하여 보여줄 수 있는 객체. 일부는 실제 객체처럼 동작.mock: 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체 stub vs mockstub은 상태를 검증mock은 행위를 검증 더 나은 테스트 작성하기한 문단에는 한 가지 주제만!'테스트 코드 = 문서' 라는 것을 항상 인지해야 한다.완벽한 제어시간, 랜덤 등은 외부에서 주입받도록 리팩토링 하기. 강의 예제의 가게의 영업시간을 테스트했던 것을 기억하자.외부 세계와 소통해야 하는 것은 mocking을 이용한다.독립성 보장하기한 테스트 메서드에는 한 가지 기능만 테스트하기given에서는 가급적 순수 생성자 또는 빌더를 통해 생성하기검증이 있는 팩토리 메서드는 사용 시 예외 발생 가능성이 있다.테스트는 순서 보장이 안 되어 있다.Fixture 구성@BeforeEach, @BeforeAll중복 코드를 줄일 수 있지만 테스트 간의 결합이 생겨버린다.신중한 사용이 필요!Fixture 클렌징deleteAll vs deleteAllInBatchdeleteAllInBatch(): 중간 테이블에 외래키 등으로 인한 오류 발생 때문에 데이터 삭제 순서를 고려해야 함. → 코치님의 추천 메서드deleteAll(): 순서 고려 필요가 없고 연관 테이블을 자동적으로 같이 지워주지만, 삭제 쿼리가 복잡하게 나간다! 전체 테이블을 조회하고 건별로 삭제하는 방식이기에 불필요한 쿼리가 늘어난다.@ParameterizedTest케이스 확장할 때 좋은 방식!재고 타입 테스트할 때를 기억하기!@CsvSource로 제공된 데이터를 @ParameterizedTest가 붙은 메서드의 파라미터에 각각 제공 가능@CsvSource 외에도 @MethodSource, @ValueSource 등이 존재@DynamicTest일련의 시나리오를 테스트@TestFactory가 붙은 메서드에서 Iterable한 반환 값을 던지는 형태 공통의 환경에서부터 일련의 사건을 실행시켜 테스트가 가능.given/when/then 세트가 복수로 생기기 때문에 가독성 저하에 주의하기.환경 통합하기gradle 탭의 tesk에서 test를 수행하면 전체 테스트를 수행해보기전체 테스트 시 Spring Boot가 여러번 로딩하게 되면 비용과 시간 증가...! 서버가 발생하는 횟수를 줄여야 한다!서버를 띄우는 @SpringBootTest과 profile을 지정하는 @ActiveProfile을 별도의 상위 클래스에 붙여 Test 클래스들에 상속시키기!mock도 별도의 환경으로 취급하기 때문에 mock 객체는 상위 클래스에서 protected로 생성하거나 mock이 필요한 환경을 별도로 설정하기.환경 통합을 위해, @DataJpaTest보단 @SpringBootTest를 사용하기private 메서드를 테스트하기결론적으로는, 할 필요도 없고 하려고 해서도 안된다!그래도 고민된다면, 객체를 분리할 시점인지 관해 생하기분리가 필요하다고 판단된다면 객체를 별도로 분리해 private 메서드를 공개 메서드로 작성하면 된다.private 메서드는 외부에서 공개하고 싶지 않은 것이고 외부에서는 몰라도 되는 부분이다! public 메서드를 검증하다보면 자연스레 검증되는 부분이다!+ α헥사고날 아키텍쳐도메인 주도의 아키텍쳐QueryDSLJPA 사용 시 거의 필수타입 체크, 동적 쿼리(값에 null이 들어올 시 자동으로 동적으로 조회해줌)가 가능낙관적 락, 비관적 락CQRS(Command and Query Responsibility Segregation)DB에서 조회와 업데이트를 분리 미션미션 5미션 6미션 5, 6은 직접 테스트 코드를 작성했던 네 번째 미션 보다는 훨씬 편안한 내용이었다. 강의 내에서 이미 정답을 다 알려준 느낌이고, 내 나름의 언어로 정리해보는 느낌이었다.쉽다고 만만히 보았기 때문일까... 특강 때 공통 피드백을 받고 바로 아차 싶었다. @BeforeEach 부분은 단순 중복 제거를 위한 것이 아니라는 점을 간과했다. 리팩토링 강의에서부터 가장 강조하신 것 중 하나가 도메인인데 그것을 놓치다니...🤣🤣 정답은 맞췄어도 풀이 과정 때문에 반타작 밖에 못한 기분이었다. 후기벌써 4주차, 마지막 발자국이자 워밍업 클럽 2기의 마무리이다. 한 달이라는 시간이 쏜살같이 지나가버렸다. 테스트 코드와 리팩토링에 대해 심도 있게 다가간 것은 처음이었던 나에게는 정말 어려웠다. 강의에서 들었던 내용을 과제에 적용하는 과정은 험난했다. 과제를 모두 제출하고 완주를 한 것 만으로도 만족해버리고 말았다. 당장 다음주 시작부터, 새로운 스터디가 시작된다. 같이 워밍업 클럽에 참여하신 분의 자바 스터디인데, 기초부터 다시 다잡는다는 느낌이라 굉장히 기대가 된다. 👏👏마지막으로, 이런 자리를 마련해주신 인프런과 코치님, 그리고 내 부족한 미션 피드백을 해주신 다른 스터디 참여자 분께 감사의 말씀을 드리고 싶다. 언젠가 3기가 열린다면, 다시 한 번 도전하고 싶다!
백엔드
・
워밍업클럽
・
백엔드
2024. 10. 20.
0
[인프런 워밍업 스터디 클럽 2기 백엔드] 3주차
해당 글은 인프런 박우빈 강사님의 「Practical Testing: 실용적인 테스트 가이드」을 바탕으로 작성하였습니다. 3주차 요약 테스트의 필요성테스트란 본래 귀찮은 존재이지만, 꼭 거쳐야만 한다...!테스트 코드가 없으면 변화의 순간마다 모든 케이스를 고려해야만 한다.그 변화들에 대한 내용을 모든 팀원이 알고 인지하고 고민해야 한다.안정성 보장이 불가능해진다. 수동 테스트의 문제커버 불가능한 영역이 발생한다.경험과 감에만 의존한 코드가 되어버린다.피드백이 늦어진다.유지 보수가 어려워진다.⇒소프트웨어에 대한 전체적 신뢰도가 낮아진다. 엉망인 테스트 코드의 문제안정성 제공이 어려워진다. 테스트 코드 자체의 유지 보수가 어려워진다.잘못된 검증이 일어날 수 있다. 올바른 테스트 코드?자동화 테스트를 통한 빠른 버그가 발견 가능하다.수동 테스트에 드는 비용이 절약된다.소프트웨어의 빠른 변화를 지원한다.테스트 코드 작성할 때는 정말 오래 걸리고 번거로운 작업이지만, 멀리 보면 결과적으로 가장 빠른 길이 된다. 아무리 귀찮고 간단한 로직이라도 항상 무조건 테스트해야 한다. 단위 테스트작은 코드 단위(클래스/메서드)를 독립적으로 검증하는 테스트검증 속도가 빠르고 안정적이다. JUnit5단위 테스트를 위한 자바 진영의 테스트 프레임워크 AssertJ테스트 코드 작성을 원활하게 돕는 테스트 라이브러리풍부한 API, 메서드 체이닝을 제공한다. 테스트 케이스의 세분화암묵적이거나 아직 드러나지 않은 요구사항이 있는지 언제나 질문하는 것이 중요하다.해피 케이스와 예외 케이스의 경계 값으로 테스트를 한다. 테스트하기 어려운 파트를 구분하기현재 시간, 사용자의 입력 값 등 언제나 달라질 수 있는 값이나 외부 세계에 영향을 주는 코드는 외부로 분리한다.테스트 하고자 하는 영역을 잊지 않고 집중해야 한다.테스트 하기 좋은 함수는 순수함수다.같은 입력 값에는 항상 동일한 결과를 도출한다.외부 세계와 단절되어 있다.lombok@Data, @Setter, @AllArgsConstructor은 지양한다.@ToString은 JPA 양방향 관계 설정 시 순환 참조의 문제가 발생할 수 있어 DTO 외에서는 지양한다. TDD ★프로덕션 코드보다 테스트 코드를 우선적으로 작성해 테스트가 구현을 주도하도록 하는 방법론이다.레드 그린 리팩토링을 사용한다.레드: 구현부가 없어 실패하는 테스트 작성그린: 빠른 시간 내에 테스트를 통과하도록 최소한의 코딩리팩토링: 구현 코드를 개선하며 테스트의 통과를 유지피드백 (TDD 핵심 가치)복잡도가 낮은 테스트 가능한 코드를 구현 가능케 한다.테스트 자체의 누락 가능성이 낮아진다.잘못된 구현을 빠르게 발견할 수 있다.발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.과감한 리팩토링이 가능해진다. 테스트는 또 하나의 문서기능을 설명하는 테스트 코드 문서이기 때문에 프로덕션 코드를 이해하는 시각과 관점을 보완해준다. displayName의 작성법'~~ 테스트' 같은 문장을 압축한 형식의 작명은 지양한다.최대한 도메인 용어와 결과까지 한 문장으로 작성해, 어느 누가 봐도 이해 가능하도록 작성한다.테스트는 메서드 관점이 아닌 도메인 정책의 관점으로 바라본다. BDD ★given / when / then 형식의 시나리오에 기반한 테스트 케이스 자체에 집중한다.개발자가 아닌 사람이 봐도 이해 가능할 정도의 추상화 레벨을 권장한다. Given: 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등) When: 시나리오 행동 진행 Then: 시나리오에 대한 결과 명시 / 검증 레이어드 아키텍쳐관심사의 분리로 별도 단위 테스트를 가능하게 한다.프레젠테이션 레이어 / 비즈니스 레이어 / 퍼시스턴트 레이어 로 구분된다. 통합 테스트A 모듈과 B 모듈을 융합한 결과가 AB일지 C일지 알 수가 없기 때문에 단위 테스트로는 부족하다.⇒ 통합 테스트의 필요여러 모듈이 협력하는 기능을 통합적으로 검증한다.기능 전체의 신뢰성을 보장한다.테스트는 풍부한 단위 테스트와 큰 기능 단위를 검증하는 통합 테스트의 모음이다. 스프링은 라이브러리 ? 프레임워크 ? 라이브러리는 내 코드가 주체, 프레임워크는 프레임워크가 주체가 된다. 이미 존재하는 프레임 안에 형식에 맞게 내 코드를 끼워넣는 것 이다.스프링의 3대 개념 제어의 역전(IoC) 의존성 주입(DI) AOP - 비즈니스 흐름과 관계없는 부분을 하나로 모아 다른 모듈로 분할 JPA(자바 퍼시스턴트 API) 자바 진영의 ORM이자 인터페이스이다.여러 구현체 중 하이버네이트를 주로 사용한다.편리하지만 쿼리를 직접 작성하지 않기 때문에 어떤 식으로 쿼리가 생성 후 실행되는지 명확한 이해가 필요하다.JPA를 한번 더 추상화한 스프링 데이터 JPA 제공한다.쿼리 DSL과 조합해 많이 사용한다. (타입 체크, 동적 쿼리) ORM(오브젝트 릴레이셔널 매핑)객체 지향 패러다임과 관계형 DB 패러다임과의 불일치하기 때문에 탄생한 매핑 개념이다.이전 개발자가 객체의 데이터를 하나하나 매핑하여 저장 및 조회를 실행해야 했다면, 현재는 ORM을 통해 개발자는 단순 작업을 줄이고 비즈니스 로직에 집중할 수 있다. Persistence Layer데이터와 직접 접근하는 레이어이다.비즈니스 가공 로직이 포함되면 안 된다.데이터의 단순 CRUD에 집중한다. Business Layer비즈니스 로직을 구현하는 레이어이다.Persistence Layer와 상호작용해 로직을 전개한다.트랜잭션의 보장이 필요하다. +α재고 감소나 데이터 추가 같은 부분은 동시성 이슈가 발생 가능성이 높기에, optimistic lock 이나 pessimistic lock 등의 방법을 통해 해결한다. ← 추가 학습 필요데이터 추가 같은 부분의 동시성 이슈가 발생했을 때, 사용자의 요청을 아예 무시할 수는 없다. 데이터 엔티티의 인덱스에 유니크 인덱스 설정을 하고 시스템 내에서 자동적으로 재시도를 n회 하는 등의 방식으로 해결 가능하다. 미션미션 4의 git repository미션 4는 Readable code 강의의 예제를 바탕으로 단위 테스트를 작성하는 과제였다. Practical testing 강의 내의 예제는 꽤나 간단했기에, 이번 과제는 단순한 과제라고 생각하고 가벼운 마음으로 접근했지만 역시나 만만치 않았다. 특히나 지뢰찾기 게임 프로젝트도 스터디 카페 프로젝트도 사용자의 입력에 관여하는 로직이 많아 mock 객체를 사용하지 않고 테스트하는 방법이 도무지 떠오르지 않아, 결과적으로는 내가 직접 접근이 가능한 부분에 대해서만 짧게 테스트 코드를 작성하는 것으로 마무리했다. 어떤 클래스에서도 모든 케이스에 대한 테스트를 진행하지 못한 것이 너무나도 아쉬웠다.😂
백엔드
・
워밍업클럽
・
백엔드
2024. 10. 13.
0
[인프런 워밍업 스터디 클럽 2기 백엔드] 2주차
해당 글은 인프런 박우빈 강사님의 「Readable Code: 읽기 좋은 코드를 작성하는 사고법」을 바탕으로 작성하였습니다. 2주차 요약해당 블로그 글에는 강의에 등장하는 예시 코드는 작성하지 않고, 이론적인 부분만 요약해 작성합니다. 예시 코드는 우빈 님의 git repository에서 다운 받을 수 있습니다. 주석의 양면성 - 주석은 죄악이다 vs 어느 정도는 남겨라!주석이 많다는 것은 비즈니스 요구사항을 코드에 잘 녹이지 못했다는 이야기코드를 설명하는 주석 → 주석에 의존하는 코드!이는 적절치 못한 추상화 레벨이라는 뜻!좋은 주석이란?리팩토링 시 히스토리를 전혀 알 수 없는 코드가 난관...후대에 전할 의사 결정의 히스토리를 코드로 표현할 수 없을 때 주석으로 상세히 설명한다.자주 변하는 정보는 지양해서 작성해야 한다. 만약 관련 정책이 변하거나 코드가 변경되면 주석도 업데이트해야 한다.주석이 없는 것보다, 부정확한 주석이 더 치명적!! 우리가 가진 모든 표현 방법을 총동원해 코드에 의도를 녹이고, 전달해야 할 정보가 남았을 때 주석을 사용하는 것이다. 변수와 메서드 나열 순서변수는 사용하는 순서대로 나열한다. ⇒ 인지적 경제성을 고려한 방식메서드의 순서는 사용하는 객체 입장에서 생각하기public 메서드에서 사용되는 private 메서드를 바로 하단에 배치 하기 vs public 메서드를 모아 상단에 배치하고 private 메서드는 모두 하단에 배치하기 정답은 없지만, 코치님의 경우세는 공개 메서드를 상단에 배치하시는 것을 선호공개 메서드끼리도 기준을 갖고 배치한다!상태 변경(update 등) >> 판별(valid 등) >> 조회 순으로 배치비공개 메서드는 공개 메서드에서 언급된 순으로 배치공통 사용 메서드라면 가장 하단에 배치 패키지 정리패키지는 문맥으로써 정보 제공이 가능패키지를 쪼개지 않아도 너무 쪼개도 관리가 어렵다!대규모 패키지 변경은 팀원과의 합의를 이룬 후에 해야 한다. ⇒충돌 방지!처음 패키지 생성 시부터 잘 나눠놓자! IDE에 도움 받기포맷 정렬: ctrl + alt + l코드 품질 향상: sonarlint 플러그인 사용하기포맷 규칙: .editorconfig단, 포맷은 IDE에서 규정한 기본 포맷팅에 익숙해지는 것이 좋다!절대적인 것이 아닌, 지속적으로 개선/반영하기 미션 3과 중간 점검.코치님께서 제공해준 프로젝트 폴더 중, 스터디 카페 시스템의 리팩토링이 과제였다.git 링크 미션 3은 7장을 보지 않고 '순수 자기 힘으로 리팩토링 하기'가 목표였지만 강의를 보고 따라치는 것과 직접 이론을 적용하는 것은 큰 차이가 있었다. 5장까지가 제일 중요한 내용이라고 생각해 해당 파트까지는 최대한 적용해보고 싶었지만, 현실은 적절한 메서드 추출과 객체 분리부터가 고난이었다. 무엇보다도 아직도 일급 컬렉션을 어디서 어떻게 적용해야 할지 감이 안 와 갈 길이 정말 멀구나...! 라고 느껴졌다.강의 마지막에 은탄환은 없다, 정답은 없다. 라고 말씀해주셨지만, 아무리 생각해도 복수 정답에도 가깝게 다가가지 못한 것 같다. 사고 방식을 전환하는 강의니, 간을 갖고 꾸준히 마주해야 할 문제인 것 같다. 중간 점검 라이브에서 다른 분들의 과제와 피드백해주신 걸 보고 많은 걸 또 얻어갔다.IDE에서 메서드 추출 시 자동으로 static 이 붙어버리니, 주의해야 한다.NPE 방지를 위해 null 대신 별도의 상수 값을 넣어 처리하는 방법등등... 1시간 30분에 못 미치는 시간이었음에도, 너무나 유익한 라이브였다. 깜짝 라이브를 꼭 진행해주셨으면 하는 작은(?) 바람이 생겨버렸다.🤣🤣
백엔드
・
워밍업클럽
2024. 10. 06.
0
[인프런 워밍업 스터디 클럽 2기 백엔드] 1주차
해당 글은 인프런 박우빈 강사님의 「Readable Code: 읽기 좋은 코드를 작성하는 사고법」을 바탕으로 작성하였습니다.그 외 글에 등장하는 모든 이미지는 직접 그린 이미지입니다. 동기와 스터디 목표인프런 워밍업 클럽 0기 우수 러너로 운 좋게 선정된 후, 반년 이상이 훌쩍 지났다. 다시금 기강이 해이해지고 할 일을 미뤄간다고 (이 반년 간 대시보드가 0기를 진행할 때보다 많이 휑해진 걸 보면 마음이 쓰리다...) 생각할 무렵 2기 소식이 들려왔다. 내가 신청한 코스는 박우빈 님의 코스! 사실 우빈 님의 명성도 잘 모른 채 '테스트 코드'라는 달콤한 단어와 커리큘럼만 보고 바로 신청서를 제출해버렸다. 추후 스터디를 같이 진행하는 러너 분께서 우빈 님의 명성을 알려주셔서 놀란 기억이 있다. 나는 작년, 학원에서 5개월 정도의 부트캠프로 코딩을 처음 배웠기 때문에, 기초적인 문법만 알지 자바 컨벤션이나 리팩토링 같은 건 수박 겉 핥기 정도로 밖에 알지 못했다. 이번 기회로 우빈 님의 사고법과 노하우를 10퍼센트라도 챙겨가는 것이 이번 스터디의 소박하지만 원대한 목표라고 할 수 있겠다. 1주차 요약해당 블로그 글에는 강의에 등장하는 예시 코드는 작성하지 않고, 이론적인 부분만 요약해 작성합니다. 예시 코드는 우빈 님의 git repository에서 다운 받을 수 있습니다.❗git repository 받기❗이 부분은, 내용이 있는 것은 아니지만 개인적으로 당부하고 싶은 부분이 있다. 아직 해당 강의를 들어본 적이 없는 분이라면, 강의에서 제공해주는 파일을 받고 꼭! 로직을 천천히 읽으며 흐름을 정확히 이해하고 강의를 시작했으면 좋겠다. 이를 제대로 파악해보지 않고 처음부터 들이받았다가 바로 다음 섹션부터 후회해버렸다... 😂😂 프로그램이란?프로그램 = 설치하는 것 = 데이터와 코드의 집합체위 개념을 유념하며 추상화에 대해 깊이 알아가자. 추상과 구체추상 ⇔사물을 정확히 이해하기 위해 사물이 지닌 여러 측면 가운데 특정 면 만을 가려내어 포착위 추상의 정의에서 가장 중요한 단어는 '가려낸다.'라고 우빈 님께서는 짚어주셨다. 가려낸다는 것은 일부를 생략하고 버리는 것이며, 이는 정보를 함축하고 제거한다는 것과 같다. 추상화 레벨추상화 레벨이란 추상과 구체 사이 존재하는 레벨들이다.추상화 레벨이라는 단어만 보면 굉장히 어렵게 느껴지지만, 간단하게 생각해보자. 바로 윗 문단에서 우리는 추상을 사물의 특정 정보를 함축, 제거한다고 이해했다. 그렇다면 이 정보들을 함축, 제거하지 않은 것을 뭐라고 할까? 바로 '구체'이다. 구체의 사전적 정의는 '사물이 직접 경험하거나 지각할 수 있도록 일정한 형태와 성질을 갖춤.' 이다. 우리가 이해한 추상이란 개념에 맞춰 생각한다면 구체의 정의에서 중요한 부분은 '지각할 수 있는' 이겠지만 확 와 닿지는 않는다. 그래서 간단하게 도식화해보았다. 예시를 한 번 들어보자. 친구에게 '저녁 식사로 라면을 끓여 먹었어.'라고 한 문장으로 끝내버릴 수 있는 간단한 문장을 극단적으로 늘려보자. '물 550 ml를 냄비에 담고 가스레인지 위에 올려 불을 점화했어. 물이 끓으면 스프와 면을 넣어 3~4분을 기다리고, 완성 후 불을 소화했어. 면을 젓가락으로 집어 입에 넣고, 저작 운동을 통해 섭취하고 소화했어.' 이렇게 말한다면 대화의 흐름이 엉망일 것이다. 이 보다 더더욱 길게 말한다면 친구가 대체 무슨 말을 하고자 하는 것인지 파악조차 어려울 것이다.여기서 우리가 일상적으로 대화하듯이 말하는 '저녁 식사로 라면을 끓여 먹었어.' 라는 한 문장이 추상, 뒤의 호흡이 긴 문장을 구체라고 생각할 수 있다. 예시를 들어보면 전혀 어려운 개념이 아니라는 것을 알 수 있다. 컴퓨터 과학과 추상그렇다면 이제 한 가지 의문이 든다. '그래서 추상의 정의를 왜 이렇게 자세히 이해하려고 하는거야?' 답은 간단하다. 컴퓨터는 인간의 언어를 알지 못하고 1과 0으로만 소통할 수 있다는 것은 모두가 알 것이다. 하지만 우리는 영어로 코드를 작성하고 있다. 이 의미는 우리가 쓰고 있는 고수준 언어는 기계어를 사람이 알기 쉽도록 추상화한 것이다. 하드웨어와 운영체제 사이도, 운영체제와 애플리케이션 사이에서도 마찬가지이다. 컴퓨터 과학이란 겹겹이 쌓인 추상화의 모음인 것이다. 누구나 코딩을 처음 접할 때, 메서드, 클래스 개념도 자세히 알지 못해 main 메서드에 줄줄이 로직을 다 때려넣어 본 적이 있을 것이다. 이렇게 되면 눈이 굉장히 피로해지고 로직 파악도 되지 않는다는 것도 알고 있다. 이런 것들을 적절히 추상화해 복잡한 데이터와 로직은 단순화하는 것이 이 강의의 핵심 목표인 것이다. 잘못된 추상화복잡한 로직들 중 일부를 똑 떼서 메서드로 추출하고, 클래스로 다 만들고 이름도 내가 읽기 좋게 바꿔버리자! 하고 무턱대고 리팩토링한다면 이게 좋은 리팩토링 방식일까?⇒ 그렇지 않다! 잘못된 추상화는 구체화 과정에서 유추가 안 된다! 친구가 이런 말을 했다고 해보자.친구들끼리 있을 때는 모두가 아~ 하고 단어의 의미를 유추하고 대화를 이어나갈 수 있을 것이다. 하지만 저 말을 가족들끼리 있을 때 한다면? 혹은 직장 상사와 있을 때 한다면? 사람마다 성향 차이가 있겠지만 아마 대부분은 대화의 흐름이 끊길 것이다. 듣는 이를 고려하지 못한 어휘 선택이라고 할 수 있다. 즉, 우리는 단어의 선택 조차도 대화하는 집단이 어디냐에 따라 적절히 선택하고는 한다. 이를 코드에 적용하자면 우리가 담당하는 도메인 영역에 따라 추상화 기준이 다를 수도 있다. 가장 중요한 것은 문맥을 파악해야 한다는 것이다. 이름 짓기이름 짓기는 추상화의 중요한 첫걸음이다.끝에 -(e)s를 붙여 데이터가 단수인지 복수인지 구분하기이름 줄이지 않기가독성 vs 효율성 에서 가독성을 택하는 게 좋다.단, 관용어 처럼 이용하는 것이 있으니 내가 속한 집단에 잘 맞춰가야 한다.은어/방언 사용하지 않기현재 팀원들이 다같이 아는 단어여도, 신입이 왔을 때를 고려해야 한다.도메인 용어 사용하기상점을 store라고 할지, shop이라 할지 우선적으로 논의해야 한다.좋은 코드를 보고 습득하기비슷한 상황에서 자주 사용하는 단어, 개념을 습득한다.poop, candidate, threshold 등... 메서드와 추상화한 메서드에는 하나의 기능만!메서드의 이름으로 구체적 내용을 추상화추상화된 구체를 유추 가능한 적절한 의미가 담기는 이름파라미터랑 연결지어 더 풍부한 의미 전달 가능(ex: ~~From (파라미터))항상 동사로 시작해야 한다는 강박은 불필요! ★메서드 선언부★반환 타입 메서드명 (파라미터) {구현부} 에서 메서드 명과 파라미터를 합쳐 메서드 시그니쳐라고 한다.하나의 세계(=메서드) 안에서는 추상화 레벨이 동등해야 한다. 파라미터파라미터의 타입, 개수, 순서를 통한 의미 전달★파라미터는 외부 세계와 소통하는 창요리 레시피의 재료 같은 의미 반환 타입메서드 시그니쳐에 납득 가는 적절한 타입의 반환 값 돌려주기void 대신 충분히 반환할 값이 있는지 고민하기반환 값이 있어야 테스트가 용이하다. 매직 넘버, 매직 스트링의미는 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등을 의미매직 넘버, 매직 스트링을 상수로 추출해 이름을 짓고 의미 부여가독성과 유지 보수성↑ Early return일찍 리턴할 수 있는 것은 빠르게 리턴!if 문의 else를 지양한다.if 문단을 메서드로 추출 후, 조건을 충족 시 바로 return 시키는 방식 사고의 depth 줄이기중첩 분기문, 중첩 반복문 줄이기각각의 반복문을 메서드로 쪼갠다!무조건 depth를 1로 만들자는 의미 X!2중 중첩이 더 도움이 된다고 판단 시 메서드 분리는 금물.사용할 변수는 가깝게 선언하기i = 10; ... 무언가의 복잡한 로직 ... j = i + 10; // 이렇게 되면 i의 값이 기억이 안 난다!메서드 리팩토링 시 컴파일 에러 주의!리팩토링할 메서드를 복제해 리팩토링 완료 후 원 메서드를 지우는 방식으로 진행 권장 공백 라인 대하기공백도 의미를 갖는다! 의미가 달라진다고 생각하는 부분들에 공백 라인을 준다! 부정어를 대하는 자세부정어구를 사용하지 않아도 되는 상황인지 체크부정의 의미를 다른 단어가 있는지 고민하기! 같은 부정 연산자는 가독성이 떨어지니 지양해피 케이스와 예외 처리사람은 해피 케이스에 몰두하게 되기 때문에 예외 가능성을 최대한 낮춰야 한다.검증이 필요한 부분은 주로 외부 세계와의 접점사용자 입력, 객체 생성, 외부 서버 요청 등...의도한 예외와 예상치 못한 예외를 구분하기사용자에게 보여줄 예외 vs 개발자가 처리해야 하는 예외 null을 대하는 자세자바에서는 null 처리가 중요하다. NullPointException의 지옥...NullPoint를 최대한 방지하도록 경각심 갖기메서드 설계 시 return null을 자제이것이 불가능하면 Optional 사용을 고려단, Optional은 꼭 필요할 때만 사용할 것Optional을 파라미터로 받지 않도록 하기. 분기 케이스가 3개나 되어 버린다.Optional 반환 시 빠르게 해소한다.★ orElseGet(), orElseThrow(), ifPresent(), ifPresentOfElse() 등을 사용하자★ 객체와 추상화 레벨객체의 비공개 필드와 비공개 로직은 공개 메서드 선언부로 외부와 소통 ⇒ 객체의 책임은 공개 메서드로 드러난다.책임에 따라 분리된 각 객체들이 상호작용 하며 협력한다.외부에서는 구체적 구현을 알 수 없고 몰라도 된다! 객체 생성 시 주의점1개의 관심사가 명확하게 정의되었는가? 외부와 어떤 소통을 해야하는가?생성자, 정적 팩토리 메서드에서 유효성 검증 가능도메인 특화 검증 로직이 들어갈 수 있음setter 사용 자제!데이터는 불변이어야 사이드 이펙트가 없다!객체 내부에서 외부의 개입 없이 자체 변경 및 가공을 하도록 하자변경이 불가피하다면 update~ 같은 의미있는 네이밍을 하자.getter 사용 자제!getter도 꼭 필요할 때만 사용한다!getter는 폭력적인 메서드라 외부에서 getter를 호출하는 것은 강도를 불러오는 것과 다름이 없다!객체에 메세지를 보내기!필드 수는 적을 수록 좋다불필요 데이터가 많을수록 복잡도가 높아진다.중복 데이터 필드는 최대한 줄이고, 파생 데이터 필드는 메서드로 풀어본다.단, 미리 가공하는 것이 성능에 이점이 있다면 필드로 쓰자. SOLIDSRP(Single Responsibility) / 단일 책임 원칙 - 한 객체가 책임 1, 책임 2를 모두 갖고 있다면, SRP를 위반 - 하나의 클래스는 단 한 가지의 변경 이유(= 책임) 만을 가져야 한다. - 객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경 되는가? - 관심사의 분리 - 높은 응집도(클래스나 모듈 내 요소들이 긴밀하게 작용), 낮은 결합도(=의존성 / 한 객체가 변경되었을 때 다른 객체가 변화되는가) - 클래스와 객체 레벨에서 강조되는 원칙 - 책임을 갖는다의 의미? 지금 객체의 책임은? ← 책임이란 판단이 어려움!- 책임을 볼 줄 아는 눈이 필요 OCP(Open-Closed)- 확장에는 열려 있고 수정에는 닫혀 있기- 기존 코드 변경 없이, 시스템 기능 확장해야 한다.- 추상화와 다형성을 활용 LSP(Liskov Substitution)- 상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환 가능해야 한다.- 자식 클래스는 부모 클래스의 책임을 준수하고 부모 클래스의 행동을 변경하면 안 된다!- LSP 위반 시 상속 클래스 사용 시 오동작, 예상 밖 예외 발생 ISP(Interface Segregation)- 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.- 인터페이스 잘게 쪼개기!- ISP 위반 시 불필요한 의존성으로 인해 결합도가 높아진다.- 인터페이스에 A,B 기능이 존재하고, 구현체 1은 A,B를 모두 사용하고 구현체 2가 A만 사용한다면, 인터페이스를 A, B로 쪼개야 한다. DIP(Dependency Inversion)- 상위 수준의 모듈은 하위 수준의 모듈에 의존하면 안 된다.- 둘 모두 추상화에 의존(=의존성의 역방향)해야 한다.- 고수준 모듈이 저수준 모듈을 참조하는 것을 의존성의 순방향이라고 한다.- 저수준 모듈 변경 시에도 고수준 모듈에 영향이 가면 안된다.- 카페(고수준)에서 커피(저수준)를 판다(sell 함수)라는 행위를 한다고 가정- 카페에서 다른 음료를 판매하고 싶다면?- 음료라는 인터페이스를 둬야 한다! 상속과 조합상속보다는 조합!상속은 수정이 어렵다.부모 자식 간의 결합도가 높다...조합과 인터페이스를 잘 활용하는 것이 관건!상속으로 중복 코드 제거 하기 중복이 생겨도 유연한 구조중복 제거로 극한의 효율을 얻고자 하는 것은 옛 이야기이다. value object도메인의 개념을 추상화하여 표현하는 값 객체불변, 동등, 유효성 검증 보장이 필수불변성: final 필드 사용하기, setter 금지하기동등성: 서로 다른 인스턴스여도 내부 값이 같으면 같은 객체지폐의 일련번호가 달라도 같은 값으로 보는 것과 같은 개념이다!유효성 검증: 객체가 생성되는 시점에 대한 유효성 보장 VO? Entity?Entity는 식별자가 존재!식별자 외의 필드 값이 달라도 식별자가 같으면 동등한 객체이다.VO는 식별자가 없고, 내부 필드의 모든 값이 다 같아야 동등한 객체이다. 일급 컬렉션일급 시민다른 요소에게 사용 가능한 모든 연산을 지원하는 요소변수 할당 가능파라미터 전달 가능함수 결과 반환 가능일급 컬렉션컬렉션을 포장하면서 컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 다른 객체와 동등 레벨로 다룰 수 있다.의미 부여와 가공 로직의 보금자리가 생긴다!getter로 컬렉션을 반환해야 한다면 외부 조작을 피하기 위해 새 컬렉션을 만들어 반환해야 한다.ex) getter로 리스트를 가져와 add() 등의 함수로 조작 위험성 존재 Enum의 특성과 활용상수의 집합상수와 관련된 로직을 담는 공간! → 상태와 행위를 한 곳에서 관리 가능한 객체특정 도메인 개념에 대해 종류와 기능을 명시적 표현 가능단, 변경이 잦은 개념은 Enum 보단 DB 관리가 나을 수도 있다. + αorElse() vs orElseGet() vs orElseThrow()의 차이orElseThrow는 값이 있으면 쓰고 없으면 예외.orElse()는 괄호 안이 항상 실행되는 값이다. 확정된 값일 때만 사용한다. (호출할 필요가 없어도(null이어도...) 항상 실행) orElseGet()은 null인 경우 실행된다. 값을 제공하는 동작을 정의한다. (null인 경우에만 괄호 안 동작이 실행)- orElseGet()은 Supplier를 통해 람다식 형태로 매개변수를 전달한다. 이는 자바의 람다식 동작 방식으로 인해, 즉시 실행하지 않고 호출할 때만 실행한다. 이를 통해 orElse와 다르게 지연 평가가 가능안티 패턴강의에서 지나가듯 말씀하신 내용으로, e.printStackTrace()는 대표적 안티 패턴!나머지 대표 안티 패턴은 God Object, God Class, 매직 넘버, 매직 스트링, 싱글톤 남용 등이 존재자바의 람다식 동작 방식추가 학습 필요 미션미션은 총 2개가 있었고, 각 풀이는 노션 포스트 링크를 첨부한다.미션 1미션 2
백엔드
・
백엔드
・
워밍업클럽
2024. 03. 18.
2
[인프런 워밍업 클럽 BE] 참여 후기
이 후기글은 인프런 워밍업 클럽 0기 BE의 전체 소감문입니다.https://inf.run/Hywa 사실 후기글을 써본 경험이 거의 전무하기에, 어떻게 작성해야 하는지조차 감이 잘 잡히지 않는다. 그래서 느낀 감정들을 두서없이 그저 솔직하게 작성해볼까 한다. 참가 신청과 첫 주이번 최태현 코치님 강의는 이전에 절반만 들어놓고 반년 간 시간을 허비하며 지낸 그런 부끄러운 과거 속의 강의 중 하나였다 . 그래도 운이 좋게도 메일로 이번 워밍업 클럽 홍보 글이 날아온 것을 보았고 바로 신청했다. 나는 스스로의 실력에 확신이 차지 않으면 도전하지 못하는, 어찌보면 많이 소극적인 성격인 편이다. 그랬기에 그동안 인프런에서 팀 프로젝트를 시도해보려고 해도 개발자로서의 지식이 너무 얕은 것 같아 차마 도전해보기가 어려웠다. 이번 스터디 신청 자체가 나 나름대로의 하나의 도전이었던 셈이었다. 결과적으로는 잘한 선택이었다고 현재는 마음 깊이 생각하고 있다.디스코드에 처음 접속해 서로 인사말을 남기는데 사실 조금, 아니 상당히 당황스러웠다. 나처럼 초보자 분들이 많이 오는 것을 예상했으나 이미 현업 종사자분들도 다수 참가하시는 것을 보았다. 그때부터 발등에 불이 떨어진 기분이었다. 다른 분들의 질문의 깊이나 발자국, 과제 내용 등을 보고 압도되는 기분이었다. 😂😂그리고 대망의 첫 중간 점검 라이브 때, 일정 관리 실패로 나는 바보같이 라이브를 놓쳐버렸다. 완주 러너의 조건에 중간 점검 라이브를 모두 참가해야 한다는 조항이 있었기 때문에 일주일 만에 열심히 하겠다는 나름의 다짐이 완전히 망가졌다. 그 이후, 그리고 스터디 끝첫 라이브를 놓치고 하루는 기분이 약간 쳐지긴 했었다. 딱히 우수 러너를 노리거나 포인트를 꼭 받아야겠다! 는 아니었지만, 그래도 무언가를 완주했다는 그 성과 자체가 없어지는 것이니 뼈아픈 일이었다. (그리고 수료증을 못 받는 것도 아쉬웠고... ) 하지만 제 1의 목표는 어디까지나 학습이니 그 이후도 내 나름대로 착실히 수행했다. 완주 러너가 되지는 못하겠지만 과제도 꾸준히 제출했고 2차 점검 라이브도 모두 참석했다.이전 후기에서도 작성했듯이 미니 프로젝트 코드 리뷰도 진행해보았고 지금은 좋은 분들과 함께 사이드 프로젝트를 진행 중이다. 아직 본격적인 개발 단계 전이지만... 😊그리고 인프런과 코치님들의 배려로 완주 조건이 완화되어 무사히 완주 러너가 될 수 있었다!당연히 완주 러너가 못 될 것을 예상해 오프라인 수료식 참가 신청을 하지 않은 것은 후회하긴 했지만... 완주 러너가 된 것 만으로도 만족했다. 그리고 대망의 수료식 날, 정말 예상하지 못하게 우수 러너로 선정이 되었다. 선정해주신 코치님에게 몇 번이고 감사 인사를 드리고 싶었다. 우수 러너 혜택으로 인프콘 티켓과 코치님과의 1:1 멘토링을 얻게 되었으니 정말 너무 과분하고 감사한 보상이다. 🙇♀️🙇♀ 반성할 점과 이모저모람다 함수와 스트림 학습을 위해 새 블로그 글을 만든 것이 있는데 첫 날 이후 정말 하나도 갱신하지 못했다.😭 3/9에 SQLD 시험이 있었기에 강의, 과제, 자격증 시험 공부를 병행하면서 저 글까지 갱신하는 것은 아무래도 과한 목표였을지도 모른다. 사실 저 SQLD 시험도 진작에 공부했으면 됐겠지만... 이미 지나버렸으니 어쩔 수가 없다. 요즘은 학습한 내용을 모두 개인 노션 페이지에 정리하고 있기에 아마 추가 학습을 해도 저 블로그 글은 갱신이 안 되지 않을까 싶다.요즘은 영한님의 로드맵을 따라 쭉 강의를 듣고 있다. 사이드 프로젝트에 폐를 끼치지 않기 위해 본격적인 시작 전 최대한 기반 지식을 더 다지고 있다. 우수 러너 혜택으로 1:1 멘토링도 남아있어서 더 많은 것을 얻기 위해서라도 많은 지식을 쌓아놓아야 할 듯 하다.
백엔드
・
스터디
・
후기
2024. 03. 07.
0
[인프런 워밍업 클럽 BE]3주차 강의 정리 + 마지막 회고
이 문서는자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] 강의 - 인프런 (inflearn.com)강의와 연관 스터디 내용을 정리하였습니다 배포: 최종 사용자에게 소프트웨어를 전달하는 과정 = 전용 컴퓨터에 우리의 서버를 옮겨 실행시키는 것 지금까지는 자신의 컴퓨터에만 코드, 스프링, DB가 존재했다. 만약 누군가 우리의 사이트에 접속하도록 만드려면? 몇 가지 문제가 존재한다.컴퓨터를 24시간 내내 켜둘 수가 없다!우리 컴퓨터를 온전히 서버를 위한 것들만 실행시키고 있을 수가 없다!이 문제를 방지하기 위해서 전용 컴퓨터에 코드를 옮기고 스프링과 mysql이 설치 후 접속하게 만들 것 이다. 그렇다면 그 전용 컴퓨터는 어떻게 마련해야 할까.=> AWS를 이용해서 대여하자.AWS에서 대여 시 알아야 하는 부분이 존재한다. 우리는 보통 가정용 컴퓨터에서 mac이나 윈도우를 OS로 이용하지만, 서버용은 대부분 리눅스로 돌아간다! profile과 H2배포를 진행하기 전, 한 가지 과정을 더 거쳐보자.자신의 컴퓨터와 전용 컴퓨터에서 사용할 환경을 다르게 설정해보자. (서버 코드는 동일하다.) local profile에서는 H2 DB dev profile에서는 MySQL H2 DB: 경량 데이터베이스, 메모리에 데이터를 저장(휘발) → 개발 단계에서만 사용! spring: config: activate: on-profile: local datasource: url: "jdbc:h2:mem:library;MODE=MYSQL;NON_KEYWORDS=USER" username: "sa" password: "" driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: show_sql: true format_sql: true dialect: org.hibernate.dialect.H2Dialect h2: console: enabled: true path: /h2-console --- spring: config: activate: on-profile: dev datasource: url: "jdbc:mysql://localhost/library" username: "..." 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설정한 profile에 맞춰 실행해보자!Run/Debug Configuration > active profiles 에 적용할 profile 이름 지정설정 후 실행을 하면 아래와 같은 로그가 나온다.이제 H2 console로 접속해보자.우리가 application.yml 에 path: /h2-console 이 부분을 작성하였기 때문에, localhost:8080/h2-console 로 접속이 가능하다!JDBC url에는 yml에 작성한 url을 그대로 입력하면 된다. git/githubgit: 코드를 쉽게 관리할 수 있도록 해주는 버전 관리 프로그램github: git으로 관리되는 프로젝트의 코드가 저장되는 저장소 github 왜 사용할까? → 내 컴퓨터에 있는 코드가 알 수 없는 이유로 소실되어도 대비 가능! 전용 컴퓨터와 내 컴퓨터와의 연결점 github 간단 사용법 저장소(레포지토리) 생성이름과 간단한 설명 작성private / public 여부 설정생성!참고로 github 홈페이지에서 하나하나 레포지토리를 생성하지 않고 intellij에서 간편하게 연동하는 법도 있다!아래 블로그에 자세한 사용법이 있으니 참고하자.https://goddaehee.tistory.com/249 필수로 알아야 하는 git 명령어git init : 이 프로젝트를 git이 관리함을 알려주는 명령어git remote add origin [저장소 주소] : 저장소를 설정git add . : 변경된 모든 파일을 담아라git add 파일명 : 특정 파일만 담아라git status : 현재 상태 확인git reset : add로 담았던 파일 다시 빼기git commit -m "메시지 내용~" : add로 담았던 파일 commit (송장만 붙인 상태로 이해하자)git config --global user.email "이메일 주소" : git hub의 이메일과 github의 username 작성git config --global user.name "이름"git push --set-upstream origin master : 최초 1회 권한 허가git push : 저장소에 commit한 파일들 넣기 .gitignore : git이 관리하지 않을 폴더나 파일을 설정build/ .gradle/ .idea/ AWS EC2에서 Linux 명령어 사용 EC2에 접속하는 법다운로드 키페어 이용:우리가 접속하려는 EC2의 IP 주소, 이전에 다운로드 받은 키 페어 필요git CLI(git Bash) 혹은 Mac terminal로 접속AWS에서 직접 연결 linux 명령어mkdir : 폴더 생성ls : 현재 위치에서 파일이나 폴더 확인ls -l : 조금 더 자세한 정보 확인drwxrwxr-x (-l 옵션으로 확인 가능한 정보)맨 앞의 d: 폴더r: 읽기 권한 / w: 쓰기 권한 / x: 실행 권한폴더 소유자의 권한 / 폴더 소유 그룹의 권한 / 아무나 접근했을 때의 권한cd : 폴더 안으로 들어가기pwd : 현재 위치 확인cd .. : 상위 폴더로 이동rmdir : 비어있는 폴더 제거build.gradle의 이해: gradle을 이용해 프로젝트를 빌드하고 의존성 관리. groovy / kotlin 중 이용plugins 블럭 - 플러그인을 추가org.springframework.boot: 스프링 빌드 시 실행가능한 jar 파일이 발생하도록 함, 애플리케이션 실행을 돕고 다른 플러그인의 적용 도움io.spring.dependency-management: 외부 라이브러리, 프레임워크 버전 관리 도움, 의존성 처리group, version, sourceCompatibility - 그룹 명, 프로젝트 버전, 사용 중인 JDK 버전repositories - 외부 라이브러리/프레임워크를 가져오는 장소 설정dependencies - 사용할 라이브러리/프레임워크 표시. implementation은 의존성 항시 사용, runtimeOnly는 코드 실행시에만 의존성 사용 마치며... 드디어 스터디 마지막 주차다! 뭔가 정신없이 지나간 것 같으면서도 오랜만에 성실하고 꾸준한 학습을 한 것 같아서 보람을 느끼고 있다. 인프런에서 진행하는 스터디는 이번에 처음 참여해본 것이었는데, 망설임 없이 신청하길 잘한 것 같다.사실 이번주 강의는 미니 프로젝트 진행에 급급해 지난 주차에 비해 성실하지 못하게 학습한 것 같았다. 배포 연습 진행 중, mysql 비밀번호 수정 시 약간의 트러블이 생겨 삽질을 오래 하느라 아직 다 실습하지 못한 상태로 남아있다... 비밀번호 초기화가 마음처럼 안 된다. 우선 EC2 컴퓨터를 지우고 다시 처음부터 진행해봐야겠다.당장 토요일에 sqld 시험도 있고, 프로젝트는 기능 구현에만 급급한 상태라 에러 처리라던가 테스트 코드가 정말 미흡하다...같이 코드 리뷰 하는 분들의 테스트 코드를 보면 정말 반성을 하게 된다 😂😂 그래도 강의에서 사용하지 않은 여러 방법을 사용하거나 찾아보기도 하고 얻어가는 것도 많았다 (복합키 적용, validation 적용 등...). 자격증 시험이 끝나면 프로젝트에 미흡한 부분들을 보완하고 추가적인 공부를 하며 사이드 프로젝트를 대비해야 할 것 같다!
백엔드
・
스터디
2024. 03. 03.
0
[인프런 워밍업 클럽 BE]9-10일차 강의 이론 위주 정리 + 2주차 회고 및 과제정리
이 문서는자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] 강의 - 인프런 (inflearn.com)강의와 연관 스터디 내용을 정리하였습니다. 시작하며 벌써 2주차 스터디가 끝나가는 일요일 저녁이다. 9-10일차의 강의는 본격적으로 대출, 반납 기능을 개발했다. 11일차부터는 기본적인 배포에 관해 배우는데 굉장히 설렌다. 수요일부터 미니 프로젝트와 코드 리뷰도 시작했는데 3월 초에 sqld 자격 시험도 있어서 일정이 꼬이지 않고 잘 완성시킬 수 있을 시 걱정이 앞선다. 😂😂 참고로 9-10일차 강의는 기능 개발이 중점이라 대부분이 리팩토링, 기능 개발에 관한 것이라 이론 분량이 이전보다는 적다! 그래서 이전 글처럼 n 일차, n강 식의 형식으로 작성하지 않고 이론을 간단한 리스트 형식으로만 정리하려고 한다. - 다른 기능에서 사용할 DTO가 완전히 동일하다면?대출/반납 등, 다른 기능에서 사용할 dto의 형식이 완전히 동일하다면 재활용 vs 새 DTO 생성?-> 유연한 대처를 위해 새 DTO 생성이 권장! - 연관 관계연관 관계의 주인관계의 주도권을 가진 테이블 = 상대 테이블을 참조하고 있는 테이블(상대 테이블의 기본키를 가지고 잇는 테이블)객체 연결의 기준관계의 주인이 아닌 쪽에 mappedBy 옵션을 단다.1:1 관계ex)사람과 실거주 주소의 관계@OneToOne 사용1:N@OneToMany와 @ManyToOne 사용@ManyToOne은 단방향 사용 가능@JoinColoum - 연관 관계 주인이 사용하는 어노테이션N:M@ManyToMany 사용구조적 복잡함비직관적사용하지 않는다. 일반적으로 N:1 1:M으로 풀어서 사용CascadeCascadeType.ALL - 아래 모든 작업 전파CascadeType.PERSIST - 영속성 컨텍스트에 새로운 엔티티 추가 시 해당 엔티티와 연관된 엔티티도 함께 영속화. 새로운 부모 엔티티 저장 시 연관 엔티티도 함께 저장CascadeType.MERGE - 엔티티 상태 변경 시 연관된 엔티티 상태도 함께 변경. 부모 엔티티 업데이트 시 자식 엔티티도 업데이트CascadeType.REMOVE - 부모 엔티티 삭제 시 자식 엔티티도 삭제. CascadeType.DETACH - 부모 엔티티 분리 시 자식 엔티티 분리CascadeType.REFRESH - 부모 엔티티 새로고침 시 자식 엔티티 새로고침OrphanRemoval객체간(연관 관계의 주인와 종속된 객체)의 관계가 끊어진 데이터를 자동으로 제거하는 옵션지연 로딩영속성 컨텍스트의 4번째 속성@OneToMany의 fetch 옵션에서 설정. 기본값 lazy필요한 순간 그 때 그 때 로딩연관 관계 사용의 장점각자 역할에 집중(=응집성)타인이 코드를 읽을 시 이해하기 쉬워짐테스트 코드 작성 용이연관 관계 사용의 단점지나친 연관 관계는 성능에 문제 발생도메인 간 복잡한 연결로 인한 시스템 파악 어려움⇒ 비즈니스 요구사항, 기술적 요구사항, 도메인 아키텍처 등 다방면 고민 후 결정 과제6일차. https://www.notion.so/6-6e9b71c98126401097211cbeb7f5b21c7일차. https://www.notion.so/7-95f01dca3af244439d75e0855322e6b2 간단한 회고2주가 순식간에 지나가 버렸다. 이전보다 성장한 것이 맞는지, 여러 의문이 들지만... 시간을 되돌릴 수는 없다.😭미니 프로젝트를 진행하며 다른 분들과 코드 리뷰를 진행 중이지만 보면 볼수록 내 부족함이 체감이 된다...!강의에는 특별히 다루지는 않지만, ExceptionHandler, logback 설정 등 하나하나 찾아보지 않고도 직접 어느 정도 구현할 정도의 성장이 필요할 것 같다! 코드 리뷰를 하면서 절실하게 느낀 점 중 하나는, 코드 컨벤션을 너무 안 지키고 있다는 것이다. 항상 혼자서만 진행하다 보니 타인이 내 코드를 보고 어려워할 수 있다는 것 자체를 인지하지 못한 것 같다.
백엔드
・
스터디
2024. 02. 29.
0
[인프런 워밍업 클럽 BE]6-8일차 강의 정리
이 문서는자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지] 강의 - 인프런 (inflearn.com)강의와 연관 스터디 내용을 정리하였습니다. 6일차. 19강.왜 UserController에 JdbcTemplate을 인스턴스하지 않았는데 평범하게 실행이 되었을까? ❗ 인스턴스 클래스에 소속된 개별적 객체 Person 클래스와 Job 클래스가 별도로 있을 때, Person 클래스 안에 Job a = new Job(); 형식으로 작성하는 것을 인스턴스화, Person 클래스에 개별적인 객체가 되는 것이다. 물론 static 객체는 생성없이 이용가능하다. 어디선가 자동으로 인스턴스화해준다는 의미일텐데... UserContoller에서 JdbcTemplate은 필수적으로 필요하다.= UserContoller은 JdbcTemplate에 의존이는 @RestController가 해결하고 있다.→ @RestController는 UserController를 자동으로 스프링빈으로 등록한다. 스프링 빈서버가 시작되면, 스프링 서버 내부에 거대한 컨테이너가 생성되는데, 이 때 이 스프링 컨테이너가 관리하는 객체를 스프링 빈이라고 한다. 스프링 컨테이너스프링 프레임워크의 핵심 컴포넌트.객체의 생명 주기 관리 및 추가적인 기능 제공빈의 인스턴스화, 구성, 생명 주기, 제거의존성 주입(DI)다른 빈끼리의 연결사실 JdbcTemplate은 이미 스프링빈으로 등록되어 있다!설정한 dependency(spring JPA)가 자동 등록하고 있다.스프링 컨테이너가 서로 필요한 클래스끼리 연결해주고 있다!그럼 왜 Repository에는 굳이 JdbcTemplate을 인스턴스화했을까?⇒ Repository가 빈이 아니기 때문!⇒@Repository라는 어노테이션을 붙여 빈으로 등록하자! 마찬가지로 Service에도 @Service를 붙이자.@RestController public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } } @Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } } @Repository public class UserRepository { private final JdbcTemplate jdbcTemplate; public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } } 20강.스프링 컨테이너, 빈 왜 사용할까?책 이름을 메모리에 저장하는 API를 구현해보자. 이 때 서비스와 레포지토리는 스프링 빈이 아니어야 한다.BookController → BookService → BookMemoryRepsitory 그런데… 갑자기 클라이언트가 DB에 저장하도록 요구사항을 변경했다!BookController → BookService → BookMysqlRepsitory 이렇게 되면, 레포지토리를 변경했을 뿐인데 서비스 코드도 변경해야 한다. 만약 BookMysqlRepsitory를 쓰는 서비스 클래스가 몇 십, 몇 백 개 라면…? 문제가 점점 커진다.자바 인터페이스를 사용은 어떨까. 구현체 부분만 바꿀 수 있다고는 하지만, 어차피 서비스 코드를 손 본다는 것은 바뀌지 않는다.이제 스프링 컨테이너를 사용한다고 해보자.컨테이너는 BookMemoryRepsitory BookMysqlRepsitory를 모두 빈에 넣어두고, 선택해서 자동으로 인스턴스화해줄 수 있다!스프링이 알아서 컨트롤해준다!→ 제어의 역전(IoC)컨테이너가 자동으로 선택해 인스턴스화 해주는 과정→ 의존성 주입(DI)@Service public class BookService { // private final BookMemoryRepository bookMemoryRepository = new BookMemoryRepository(); private final BookRepository bookRepository; public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } } @Repository public class BookMysqlRepository implements BookRepository{ @Override public void saveBook() { } } @Repository public class BookMemoryRepository implements BookRepository{ @Override public void saveBook() { } } BookService와 BookMysqlRepository, BookMemoryRepository 을 모두 빈으로 등록해줬다.이제 서비스에 손을 대지 않은 채 레포지토리를 변경할 수 있다! 근데… 무슨 수로, 무슨 기준으로 선택할 수 있을까? 같은 @Repository 가 붙어있는데 말이다.스프링 컨테이너도 이런 상황을 보면 에러를 던진다! 우선 순위를 줘야한다. 사용할 스프링 위에 @Primary를 붙이자.@Primary - 빈의 우선권 결정@Primary @Repository public class BookMysqlRepository implements BookRepository{ @Override public void saveBook() { } } 21강.빈 등록 방법@Configuration클래스에 붙이는 어노테이션@Bean 사용 시 함께 사용해야 한다.@Bean메소드에 붙이는 어노테이션메소드에서 반환되는 객체를 스프링 빈에 등록한다.@Repository에 주석처리를 한 후, 직접 빈 등록을 해보자.@Configuration public class UserConfiguration { @Bean public UserRepository userRepository(JdbcTemplate jdbcTemplate){ return new UserRepository(jdbcTemplate); } } 이렇게 하면 UserService에서도 오류없이 주입하는 것을 볼 수 있다.언제 @Service, @Repository 사용?개발자가 직접 만든 클래스에 사용@Configuration+@Bean 은 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할 때 사용한다.ex) JdbcTemplate@Component주어진 클래스를 컴포넌트로 간주해당 클래스들은 스프링 서버가 실행할 때 자동 감지 후 스프링 컨테이너에 들어와, 스프링 빈으로 등록@Service, @Repository, @Controller 등 이미 여러 어노테이션에 붙어있다!(자동 감지 중~)컨트롤러, 서비스, 리포지토리가 아닌 개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용스프링 빈 주입 방법생성자 private final JdbcTemplate jdbcTemplate; public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } Setter + @Autowiredprivate JdbcTemplate jdbcTemplate; @Autowired public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } 누군가 setter에 접근해 문제가 발생할 가능성 필드에 직접 @Autowired 사용@Autowired private JdbcTemplate jdbcTemplate; 테스트가 어려워지는 요인 @Qualifier@Primary처럼 여러 후보군 중 사용할 빈을 선택@Qualifier와 @Primary를 동시에 사용한다면?→ @Qualifier 등록!(사용자가 직접 연결해주었다는 특수함) 7일차. 23강.SQL 문 직접 작성의 문제문자열 작성이라 오타가 나기 쉽고, 발견이 어려움→ 컴파일 시점에 발견 X, 런타임 시점에 발견특정 DB에 종속된다.→DB마다 문법이 조금씩 달라 오류 발생 위험반복 작업이 많다. 테이블 하나당 CRUD쿼리 반복 작성 필요.DB의 테이블과 객체는 패러다임이 다르다.교실과 학생들 객체가 있다고 가정교실에는 학생들을 담는 리스트가 존재한다.학생 역시 본인이 속한 교실에 대한 정보를 담기 위해 교실 객체를 갖고 있다. 부모 클래스, 하위 클래스 역시 DB로 표현이 어렵다.⇒ JPA(Java Persistence API) 사용으로 이를 해결 JPA(Java Persistence API)자바 진영의 ORM(Object-Relational Mapping)Persistence : 영속성서버가 재시작되어도 데이터는 영구적 저장되는 속성ORM - 객체와 DB테이블을 매핑⇒ 객체와 관계형 DB 테이블을 짝지어 데이터를 영구적 저장하도록 해주는 JAVA 진영 규칙HIBERNATE규칙을 코드로 구현, 작성HIBERNATE는 JPA를 구현하고 있는 구현체이다.내부적으로 Jdbc 사용24강.매핑 방법User객체와 user 테이블을 매핑@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id=null; ... } 위 형태로 수정@Entity - 스프링이 User 객체와 user 테이블을 같은 것으로 간주Entity - 저장되고, 관리되어야 하는 데이터@Id - 해당 필드를 PK로 간주@GeneratedValue - 자동 생성되는 PKstrategy = GenerationType.IDENTITY - mysql의 auto_increment와 동일. oracle dbms의 경우 주로 SEQUENCE를 선택Entity 객체는 매개변수가 없는 기본 생성자가 필요하다. protected여도 무방.protected User() {} @Column(name = "name", nullable = false, length = 20) private String name; @Column객체의 필드와 테이블의 필드 매핑객체 필드 이름과 테이블 필드 이름이 동일하다면 name=”” 생략완전 동일한 제약조건인 경우에는 @Column 생략 가능 JPA 사용을 위한 설정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.MySQL8Dialect ddl-auto - 스프링 시작 시 DB 테이블 처리 방법create: 기존 테이블이 있다면 삭제 후 재생성create-drop: 스프링 종료 시 테이블 모두 제거update: 객체와 테이블이 다른 부분만 변경validate: 객체와 테이블이 동일한지 확인none: 별다른 조치를 취하지 않는다.show_sql - JPA를 사용에 DB에 SQL을 날릴 때 SQL을 보여주는 것에 대한 여부format-sql - SQL을 보여줄 때 예쁘게 포맷팅 여부dialect - 하나의 DB로 특정하면 조금씩 다른 문법을 수정 25강.JPA를 이용한 자동 쿼리이전 유저 생성/조회/업데이트 기능 리팩토링이전의 UserRepository는 UserJdbcRepository로 이름 변경만 한 후,public interface UserRepository extends JpaRepository { } 위 파일 생성JpaRepository1 - 엔티티 객체2 - 해당 객체의 기본키 타입저장userRepository.save(new User(request.getName(), request.getAge())); 전체조회 List all = userRepository.findAll(); // return all.stream().map(user -> new UserResponse(user.getId(),user.getName(),user.getAge())).collect(Collectors.toList()); return all.stream().map(UserResponse::new).collect(Collectors.toList()); 주석처리된 부분과 그 하단 코드는 동일한 기능이다. 코드 단축을 위해 UserResponse에 User 가 매개변수인 생성자를 별도 생성해 작성한 것이다.조건조회//반환은 Optional userRepository.findById(request.getId()) 수정User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); //해당 유저가 존재하지 않는다면 오류를 던진다. user.updateName(request.getName()); //domain에 있는 User 파일에 updateName이란 메서드 생성 userRepository.save(user); //변경사항 감지 후 저장 JPA가 어떻게 쿼리를 처리?⇒ Spring Date JPA가 처리Spring Date JPA - 복잡한 JPA코드를 스프링과 함께 쉽게 사용하도록 도와주는 라이브러리(Simple Jpa Repository)26강.삭제User user = userRepository.findById(request.getId()) .orElseThrow(IllegalArgumentException::new); //해당 유저가 존재하지 않는다면 오류를 던진다. userRepository.delete(user); 만약 id 기준으로 찾는 것 외에, 다른 쿼리를 만들고 싶다면?JpaRepository를 상속받은 UserRepository에 가서 함수(메서드 내부 코드X)를 추가!함수 이름에 맞춰, sql이 자동 작성된다.find - 1개의 데이터만 가져온다.By - By 다음에 오는 필드명으로 ‘where filed_name=?’ 인 조건문이 붙는다.JPA 사용할 수 있는 각종 키워드find: 1건을 가져온다. 반환 타입은 객체 혹은 OprionalfindAll: 쿼리 결과가 N개, List 반환exists: 쿼리 결과 존재 확인. boolean 반환count: sql의 결과 개수. long 반환By~Filed1AndFiled2: 논리곱 연산자Filed1OrFiled2: 논리합 연산자Filed1GreaterThan: 초과Filed1GreaterThanEqual: 이상Filed1LessThan: 미만Filed1LessThanEqual: 이하Filed1Between: 사이에Filed1StartsWith: ~로 시작하는Filed1EndsWith: ~로 끝나는 8일차. 27강트랜잭션DB의 논리적 연산 단위. 1개의 트랜잭션에는 N개(≥1)의 SQL문 포함.분할할 수 없는 최소 단위. 전부 적용하거나 전부 취소하거나.트랜잭션 관련 sql문start transaction; //시작 commit; //commit rollback; //rollback 28강JPA에서의 트랜잭션 적용⇒Service 빈에서 하나의 트랜잭션으로 묶을 로직 메서드 위에 @Transcational 을 추가한다.@Transactional public void saveUser(UserSaveRequest request){ User u = userRepository.save(new User(request.getName(), request.getAge())); } @Transcational붙어있는 메서드가 시작할 때 트랜잭션을 시작예외 없이 잘 끝났다면 commit오류라면 rollback 조회 쿼리만 사용하는 경우 옵션으로 readOnly = true 를 붙여주자. 약간의 성능이 향상된다.@Transactional(readOnly = true) public List getUsers(){} ❗ IOException과 같은 Checked Exception은 기본적으로 롤백이 일어나지 않는다.=> 옵션으로 롤백 처리를 할 수 있다. 영속성 컨텍스트: 테이블과 매핑된 Entity 객체를 관리/보관하는 역할스프링에서는 트랜잭션 사용 시 영속성 컨텍스트가 생성트랜잭션 종료 시 영속성 컨텍스트가 종료 영속성 컨텍스트의 특성 4가지변경 감지(Dirty Check)영속성 컨텍스트 안에서 불러온 엔티티는 명시적으로 save하지 않더라도 변경 감지 후 자동 저장ex) 이전에 만든 updateUser 메서드에서 userJPARepository.save를 사용하지 않아도 자동 업데이트해준다.쓰기 지연DB의 입력/수정/삭제 sql을 바로 날리지 않고 트랜잭션이 commit될 때 모아서 한 번만 날린다.DB와의 통신 횟수가 줄어드는 효과1차 캐싱ID를 기준으로 엔티티를 기억DB와의 통신 횟수가 줄어드는 효과캐싱된 객체는 완전 동일(객체 인스턴스 동일!)
백엔드
・
스터디
・
백엔드
2024. 02. 25.
0
[인프런 워밍업 클럽 BE]4-5일차 강의 정리 + 1주차 과제 정리 및 짧은 회고
이 문서는 아래 강의 정리글입니다.https://www.inflearn.com/course/%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/dashboard 벌써 스터디를 시작한 지 일주일이 지나간다...! 과제는 나름대로 열심히 하고 강의도 진도표에 맞게 잘 들어왔지만... 바보 같게도 스케줄 관리 실패로 5일차의 중간 점검 라이브 참석을 못해버렸다... 최근의 일 중 가장 멍청했던 일 아닐까 싶다. 완주 혜택은 말끔하게 사라져버렸지만, 일단 끝까지 완수해보려고 한다. 꾸준히 공부하는 것 만으로도 얻는 것이 클테니까. 사실 슬슬 이론보다는 코드의 양이 늘어가기에, 이걸 어떻게 정리해야 하나, 고민이 든다. 개인 노션 페이지에는 마음껏 코드를 올리고 있지만, 이곳에서는 최대한 이론만 들고오려 했기에. 그래도 나름대로 필요하다고 생각하는 내용 위주로 정리해보겠다. 4일차 14강.업데이트HTTP 메서드 - PUTHTTP path - /userHTTP body - JSON결과 반환X변경은 이름만 변경할 예정이다.-> 식별자인 id와 변경하고 싶은 이름을 가져와 업데이트하기 때문에, request에는 id와 name이 필요 @PutMapping - PUT API를 위한 어노테이션String sql = ""; jdbcTemplate.update(sql, ...); 삭제HTTP 메서드 - DELETEHTTP path - /userHTTP 쿼리 - 문자열 name결과 반환X->이름을 기준으로 삭제한다.@DeleteMapping - Delete API를 위한 어노테이션 현재 개발 API의 문제사실 현재 API에는 큰 문제에 있다. 직접 페이지에서 실행했을 때는 문제가 없어 보이지만 포스트맨 같은 곳에서 존재하지 않는 사람에 대해 변경 혹은 삭제를 시도할 경우, 오류가 아닌 200ok를 내고있다.-> 예외 처리가 필요! 15강.예외 처리예외 처리를 어떻게 할까? 방식 자체는 간단하다. 자바 기초 문법을 배울 때부터 자주 접하는throw new IllegalArgumentException(); //500 내부 서버 오류를 던져준으로 해결된다. 근데 아무 곳에서나 예외 처리를 할 수는 없다! 이 데이터의 존재 여부를 확인 후, 예외 처리를 해야한다. 즉, 《조회 쿼리로 데이터가 있는 지 확인 => 예외 처리》 순서로 간다.String sql = "특정 조건에 대한 조회 쿼리"; boolean isNotExist = jdbcTemplate.query(sql, (rs,rowNum) -> 0, request.getId()).isEmpty();조회 결과가 있을 경우 0이 담긴 리스트가, 없다면 빈 리스트가 나온다는 것을 이용해, isEmpty() 함수로 boolean으로 반환되도록 한다.만약 isNotExist 가 true인 경우는, 오류를 던지고 그 외에는 정상 처리를 한다. 5일차. 17강.Clean CodeCode는 요구사항을 표현하는 언어→ Clean Code가 왜 필요한가?→ 실무에 가면 코드를 읽는 것을 피할 수 없다! 그러니 누가봐도 이해하기 쉬운 코드를 쓰자!스파게티 코드가 되는 것을 막아야한다!⇒그렇다면 어떻게 클린 코드를?⇒클래스는 작아야 한다, 그리고 하나의 책임(단일 책임 원칙)만을 갖게 한다!+ 객체 지향 설계의 5대 원칙 (SOLID) 에 대해 추가적으로 알아보자.https://www.nextree.co.kr/p6960/링크를 건 사이트 외에도 다양한 곳에서 정보를 얻을 수 있다! 여기까지만 말해도 느껴진다. 이전에 짠 예제 코드는 컨트롤러 내부에서 너무 많은 처리를 하고 있다…! DB와 연결하고, 쿼리를 실행하고, 필요하면 추가적인 처리를 하고, 결과를 변환해서 반환하고…⇒ 답은 간단하다! 역할을 분리하여 쪼개기! 18강.ControllerAPI의 진입 지점 / HTTP body를 객체로 변환사용자의 요청을 받고, 처리 결과를 반환Service애플리케이션의 비즈니스 로직요청 이행을 위한 도메인 객체들이 협력 수행하도록 조정예외 처리 역시 서비스에서!Repository외부와의 통신 담당 public class UserRepository { private final JdbcTemplate jdbcTemplate; public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }public class UserService { private final UserRepository userRepository; public UserService(JdbcTemplate jdbcTemplate) { userRepository = new UserRepository(jdbcTemplate); } }public class UserController { private final UserService userService; public UserController(JdbcTemplate jdbcTemplate) { this.userService = new UserService(jdbcTemplate); } } }그 외 메서드 분리는 강의를 직접 들으며 공부하자! 과제1-5일차 과제는 개인 노션에 별도 정리했기 때문에 과제 링크를 남긴다!1일차 - https://www.notion.so/1-35973c9bdd624337b2aedfc1605245262일차 - https://www.notion.so/2-644f4434de87438d8c282edd2e9d39bd3일차 - https://www.notion.so/3-31c88c0c9fed403281340735b1b01b454일차 - https://www.notion.so/4-722e922685c04b6c96675bc0931999985일차 - https://www.notion.so/5-7013168ec49f4069a051e05f92f8b82c마치며나는 사실 스터디 시작 전에도 해당 강좌를 절반 정도 이미 수강했었다. 하지만, 조금 더 기초부터 착실하게 시작할까 하면서 다른 강의를 수강하다 자격증을 준비하다... 꽤나 끝을 보지 못하고 난잡한 목표의 삶을 살고 있었다.그런데 강의를 들으면서 새로운 과제도 하고, 질문 답변을 하며 열정을 보이는 다른 사람들을 보며 조금씩 동기부여가 되는 기분이었다. 그것과는 별개로 많이 부족한 자신을 뼈저리게 맞닥뜨리기도 했다. 과제는 제출하고 의문이 든 것에 관해 내가 한 질문 혹은 다른 분들이 질문한 것에 대한 코치님의 답변을 받을 때마다 역시 최선의 답이 아니었구나, 싶은 후회의 연속이었다. 특히 리서치 위주의 조사는 시간 부족으로 너무 겉만 핥고 지나간 것들도 많은 것 같아 아쉬웠다. 그래도 좋았던 점을 말하자면 과제를 하다 스스로 두루뭉술하게 알고 있던 키워드는 직접 조사하며 개념을 다잡을 수 있었다. 무엇보다 좋은 점은 다른 수강생분들과 같이 사이드 프로젝트를 할 기회를 얻었다는 것! 또 다른 소소한 장점이라면, 강의를 정리하면서 마크 다운 문서를 나름대로 빠르게 작성해나갈 수 있게 되었다.
백엔드
・
스프링
・
스터디
2024. 02. 22.
1
[인프런 워밍업 클럽 BE + ]람다식/스트림 추가 학습하기!
3일차 과제는 조사 과제로, 함수형 인터페이스/람다/스트림 등의 광범위한 키워드를 알아봐야 했다. 검색하면 할수록 부족한 내 자신이 느껴졌다😂. 람다식의 문법 체계와 스트림 API를 문서로만 보았을 때는 나름 할만한데? 라는 생각이 들다가도, 직접 코드를 접하면 이해가 빠릿하게 되지 않는다니... 역시 나는 보는 것 만으로는 50프로도 흡수를 못하는 것 같다. 직접 예제를 만들어보기에는 개념도 겨우 잡는 학생이 시험 문제를 낸다는 느낌이기에, 어떻게 효과적으로 코드 작성하는 걸 공부할 수 있을까? 라는 고민에 빠지다 문득 생각이 들었다. 람다식과 스트림이라는 개념 자체를 몰랐을 적, 프로그래머스에서 나는 몇 줄에 거쳐 해결한 문제를 누군가가 스트림으로 한 줄 만에 풀이를 끝내는 걸 본 기억이... 그래서 나도 그 기억에 따라 프로그래머스의 기초 문제를 람다식과 스트림 API을 통해 푸는 연습을 해보기로 했다!연습 범위는 lv0~lv1. 두 개념을 사용할 수 있는 문제들만 임의로 골라서 꾸준히 풀기!❗문제 전문이나 정답 코드는 작성하지 않는다. 어디까지나 개인 연습용이라 최고 효율적인 답을 기대하고 풀이하지 않습니다. 문제가 막힐 때, https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html 등의 문서 참조'다른 사람의 풀이' 보기!(최후 수단) 1.IntStream을 이런 방식으로도 사용할 수 있다는 생각이 미처 닿지 못해, 부끄럽게도 첫 문제부터 다른 사람의 풀이를 참고하였다. 이번 키워드 메서드를 한 번 보자.IntStream.range(0, strArr.length) .mapToObj(...) .toArray(...);IntStreamrange(n1, n2) - [n1,n2) 까지 순서대로 정렬된 연속된 값을 가진 IntStream 반환[n1,n2]의 경우에는 rangeClosed(n1, n2)를 사용한다.mapToObj(IntFunction mapper) - 스트림 요소에 주어진 함수를 적용한 결과로 구성된 객체 값 반환 정확성 테스트 결과는 위와 같다.이번 문제는 일반적인 반복문을 사용했을 때 압도적으로 빨랐다. 반복문 처리 하였을 때의 정확성 테스트 결과 이미지이다.
프로그래밍 언어
・
개인학습