블로그
전체 82024. 11. 07.
1
[인프런 워밍업 스터디 클럽 2기_BE] 후기
이번 인프런 워밍업 클럽의 목표는 포기하지 않고 완주하는 게 큰 목적이었다.인프런 워밍업 클럽 2기를 하며 크게 느낀 점이 많았다.클린코드와 테스트 코드에 대한 지식을 얻었다!사실 지식을 얻었다고 했지만 매우 버겁다. 아주 많이 소화하다가 체할 거 같다. 그래도 체득한다면 나는 우빈님처럼 멋진 개발자가 될 거라 생각.....한...다.특히 인상적인 것은 코치님의 말 중 객체에 대한 무례한 행동이 기억에 남았던 것 같다 나는 지금까지 객체를 너무 막대했던 거 같다!! 앞으로 객체와 친해지면서 예의 바른 행동을 해야겠다.자신감을 얻었다!공부를 진행하면서 우빈님이 강의에서 말한 의도나 생각들을 조금이나마 읽을 수 있어서 좋았고 더 나아가 우빈님의 개발 루틴을 볼 수 있어서 좋았다. 새로운 개념에 대해 배워나가며 적용하는 과정이 너무 재밌었고 그 과정에서 자신감이 많이 상승했었던 것 같다.후기판교와 인프런의 공기를 느끼기면서 좋은 기운을 받기 위해 워밍업 클럽 장소 갔었고 도착 후 다양한 개발자분들과 피자를 먹으면서 소통했고, 각각의 생활을 들으며 나도 더 열심히 해야겠다. 라는 생각을 했다. 피자를 다 먹고 난 후강의에서만 보던 우빈님을 실물로 보니 훨씬 더 잘 생겼었다.우빈님의 실시간 Q&A와 질문들을 들으며 공감하는 내용도 많았고 새로 배웠던 점도 많았다. 마지막으로 우수 러너를 발표하는 순간 어떤 분들이 받을지 궁금했는데 내 이름이 적혀있는 것을 보고 어? 내 이름이 동일한 사람이 있냐는 생각과 함께 얼어붙었다. 내가 받아도 되는 상인가를 많이 생각했을 것 했었다.우수 러너 받기 전 걸어가면서 사진 찍는 과정까지 너무 떨려서 기억이 나지 않았다. 상을 받으면서 그래도 꾸준히 한 보람이 있다. 방향성만 맞는지는 모르지만 그런데도 천천히 걸어도 괜찮다는 확신을 갖게 되었다.정리집에 가는 길은 꿈만 같았고 개발자 하길 잘했다고 많이 생각했다.다음 공부는 .. DDD 심화 학습과 간단한 토이 프로젝트를 하며 체득하는 과정을 할 예정이다.인프런 워밍업 스터디 클럽에서 배운 학습 내용을 가지고 개념을 습득하는 과정을 진행해야겠다.
2024. 10. 27.
0
[인프런 워밍업 스터디 클럽 2기_BE] 4주차 회고록 정리
Presentation Layer외부 요청을 가장 먼저 받는 계층에서는 파라미터에 대한 최소한의 검증을 진행한다.테스트 진행 방식비즈니스 로직과 영속성 계층은 스프링을 띄워 테스트하고, Presentation Layer는 Mocking 기술(MockMVC)을 활용한다.MockMVC란?의존성이 복잡해 실제 테스트가 어려운 경우, 스프링 MVC의 동작을 가짜 객체로 재현할 수 있는 테스트 프레임워크 요구사항관리자 페이지에서 상품 등록 가능상품명, 타입, 상태, 가격 등 입력받음 Native Query 작성 이유TDD를 염두에 두고 테스트 -> 구현 -> 리팩토링 습관화서비스, 레포지토리 코드가 얇으면 비슷하지만 코드가 많아질수록 차이가 생기므로 사소한 부분도 테스트 작성 권장.동시성 이슈가 있다면 UUID 등을 활용.@Transactional읽기 전용 설정 시, 조회만 가능하고 CUD는 제한됨. JPA는 1차 캐시에 스냅샷을 생성해 변경을 감지하지만, 읽기 전용 시 성능 이점이 있음.CQRS (Command/Read 분리)CUD와 R은 8:2 비율로 책임을 분리하고, 마스터(쓰기)/슬레이브(읽기) DB로 나누어 엔드포인트를 관리할 수 있다.@EnableJpaAuditing @Configuration public class JpaAuditingConfig { } 전역 설정을 클래스로 나누어 관리.기본 생성자를 이용해 역직렬화를 진행하고, 컨트롤러 테스트 코드는 다음과 같이 작성:mvc.perform( post("/api/v1/products/new") .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) .andExpect(status().isOk()); 규격화된 응답을 위해 ApiResponse 클래스를 사용해 공통 응답을 반환. 기존의 ResponseEntity와 차이점을 확인.NotNull, NotEmpty, NotBlank 차이점성격에 따라 검증 위치를 분리하는 것이 중요하며, 하위 레이어가 상위 레이어를 알아야 하는 상황을 피하기 위해 서비스용 DTO를 따로 작성하고, 컨트롤러에서 toServiceDTO로 변환해 처리.다른 API의 의존성 문제를 피하기 위해 DTO를 통한 책임 분리 중요요구사항매출 통계에 대한 메일 전송을 하는 요구사항 // stubbing when(mailSendClient.sendEmail(any(String.class), any(String.class), any(String.class), any(String.class))) .thenReturn(true); String의 값의 어떤 것이든 좋다! 라고 해서 테스트를 진행하는 방법TestDoubleDummy : 아무 것도 하지 않는 깡통 객체Fake : 단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체Stub : 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체 그외에는 XSpy : Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체 일부는 실제 객체처럼 동작시키고 일부만 StubbingMock : 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체Stub vs Mock가장 큰 차이는 Sutb은 상태 검증,Mock은 행위검증이다.꿀팁Mail같은 긴 네트워크를 탈 서비스 일때 트랜잭션을 걸지 않는것이 좋다.@Mock: 테스트에서 가짜 객체 만들어서 의존성 대체할 때 사용.@Spy: 실제 객체 쓰되, 필요한 메서드만 Mocking 가능.@InjectMocks: @Mock이나 @Spy로 만든 객체들 주입해서 테스트 대상 생성.BDDMockito TestBDDMockito도 있다.Mockito와 동일하지만 BDD스타일의 기능만 따랐기때문에 모든 동작이 동일하다.평소에 BDDMockito를 사용하는게 좋을거같다!1. 한 문단에 한 주제!케이스가 많아지면 복잡한 방법보단 간단히 나누는 게 좋고, @DisplayName은 추상화보다는 명확하게 써야 한다.2. 완벽하게 제어하기createOrder처럼 오버로딩된 건, 제어가 안 되면 상위 레벨로 올려서 테스트 환경을 만들어야 한다.3. 테스트 환경의 독립성 보장다른 API를 가져다 쓰면 의존성 올라가서 테스트 독립성이 떨어짐. 환경이 깨지지 않게 보장 필요.stock1.deductQuantity(1); 이렇게 수량 바꾸면 테스트 깨짐. 실패는 when이나 then에서만 나와야 하고, 길어지면 추적 어려워짐.생성자 기반이 좋고, 순수 빌더나 생성자 방식 추천. 팩토리 메서드는 지양.4. 테스트 간 독립성 보장테스트 간 자원 공유 안 하는 게 좋고, static으로 인스턴스 쓰지 않기. 필요하면 5번처럼 해라.5. 한눈에 보이는 Test Fixture 구성원하는 상태로 고정시키는 객체들로 beforeAll, BeforeEach, AfterEach가 대표적.Fixture 수정 시 모든 테스트에 영향 미치니 조심해서 사용.언제 쓰면 될까?테스트 이해에 문제 없거나, 수정해도 영향 없을 때.필요한 구성요소만 넣고, 최소한으로 관리.Test Fixture 클렌징DeleteAll vs DeleteAllInBatchdeleteAll은 N개 반복으로 속도 느려짐, DeleteAllInBatch가 더 빠름.deleteAll은 순서 상관없이 다 지워줘서 안전.Repository delete 순서 조정Product PK가 Order에 묶여 있어 참조 문제 발생. 순서 맞춰 삭제 필요.@Transaction은 평소에 쓰고, Batch 통합 테스트는 상황에 따라 사용.@ParameterizedTest케이스 확장 시 값이나 환경 바꾸고 싶을 때 사용. 변수 없이 어노테이션만으로 편리하게 사용 가능.@DynamicTest시나리오 테스트 진행 시 사용. 서버 여러 번 실행돼 속도 느려지지만, 테스트 환경 맞춰 설정하면 OK.public abstract class IntegrationTestSupport{}Mock 사용 시 새로운 서버 구성 필요하므로 상위 클래스로 올리거나 테스트 환경 두 개로 나눠서 사용.private 메서드는?테스트 안 해도 된다.객체를 분리하는걸 고려해야한다.!!RestDocsAPI 문서를 자동으로 생성하는 도구 테스트 코드와 연계해 실제 요청·응답을 기반으로 신뢰성 높은 문서를 만들어줌!과제를 진행하며 given then when을 어떻게 나누고 Test Fixture을 어떤 방식으로 나누는지 고민을 많이했다.강의에서 보던 코드를 나누는건 할만했지만 직접 나누려고 생각을 하다보니 조금 힘들었던 부분이 많았다. 또한 Mocking에 대한 정확한 이해가 되지 않았는데 개념들을 정리하다보니 글을 정리하는 순간순간 이렇게 쓰면되겠다! 라고 생각하게 됐다.마지막 주차고 많이 밀렸기도 했고 힘든 부분도 많았지만 또 뒤돌아본다면, 나에게 돌아오는 실력들이 근육처럼 붙을꺼라 생각한다!관련 코드 : https://silvercastle.notion.site/18-12a1dc39fd1e80c3914ac646daf52996?pvs=4
2024. 10. 20.
0
[인프런 워밍업 스터디 클럽 2기_BE] 3주차 회고록 정리
테스트 코드를 작성하지 않음으로써의 문제점변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야한다.변화가 생기는 매순간 마다 모든 팀원이 고민을 해야한다.빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.근데 테스트 코드가 더럽다면?프로덕션 코드의 안정성을 제공하기 힘들어짐테스트 코드 자체가 유지보수하기 어려운 짐이된다.잘못된 검증이 나올수 있다.가까이 보면 멀어보이지만 , 멀리서 보면 빠르다.단위 테스트 도입여기서 단위 테스트란?작은 코드 단위를 독립적으로 검증하는 테스트 ( 클래스 or 메서드 )검증 속도가 빠르고 안정적이다.JUNIT5테스트 단위를 위한 테스트 프레임 워크AssertJ테스트 코드 작성을 원할하게 돕는 테스트 라이브러리풍부한 API 메서드 체이닝 지원!TDD란?프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론Red-Green-Refactor실패가 되는 테스트 작성테스트 통과 최소한의 코딩 - 일단 굴러만 하기만 하면된다.구현 코드 개선 테스트 통과 유지1,2,3번 사이클이 계속 돕니다.테스트→ 기능 → 테스트기능을 먼저하면테스트 자체의 누락 가능성특정 테스트 케이스만 검증할 가능성잘못된 구현을 다소 늦게 발견할 가능성테스트를 먼저 작성하려면? → 요구사항분석과 테스트 단위를 잘 쪼개야한다.테스트를 하기 위한 구조생각이 떠오를 수 있다.클라이언트 관점에서의 피드백을 주는 Test Driven[]은 뭘까..내 생각은 철저한 요구사항이라고 생각한다.강의에서는 문서! 라고 하는거봐서 유사하다고 생각했다.문서?프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격 시켜, 모두의 자산으로 공유 가능팀으로 일하기 때문에 팀이 보기 편하게 봐야한다!잘보려면 어떻게 해야할까?저번 강의에 대해 추상화 레벨을 맞춰 가독성을 좋게하는게 포인트다!마찬가지로 문서를 보기 편하게 하기위해 테스트 코드에서는 DisplayName이 있다!Displayname은 구체적으로 섬세하게 작성해야한다.Persistence Layer타 ORM으로 변경될 수 있기 때문에 이를 보장하기 위해 테스트를 진행해야 한다.미래에 어떤 형태로 변할지 모르기 때문에, 이러한 가능성에 대비해 테스트를 확실히 해야 한다.Business Layer비즈니스 로직을 구현하는 역할Repository와 상호작용을 통해 비즈니스 로직을 전개트랜잭션을 보장해야 한다비즈니스 레이어 테스트는 레포지토리 테스트와 함께 작성하는 것이 목표이다.테스트를 위해 Request 객체를 생성자로 만들어 놓는 경우도 있다.Order 계산 부분에 대한 테스트가 필요할 것이라는 생각이 중요하다.서비스 로직에서는 등록 시간을 처리하기 어려울 수 있는데, 이 경우 파라미터로 시간을 받아 작업하는 것이 좋은 방법이다.메서드 단위 테스트는 메서드를 잘 나눠야 나중에 테스트하기 편하다.테스트를 동시에 실행할 때 하나는 성공하고 다른 하나는 실패하는 이유는 중복된 동작이 영향을 주기 때문이다.따라서 매 테스트마다 초기화를 진행해야 한다.Teardown 순서가 올바르지 않으면 문제가 생길 수 있다.@DataJpaTest와 @SpringBootTest의 차이는 트랜잭션의 차이로, 자동으로 롤백 처리가 된다.직접 트랜잭션을 설정하면 롤백이 되며, 나중에 강의에서 문제에 대해 설명해 준다.간단한 테스트도 언제든지 바뀔 수 있기 때문에 미래를 대비한 테스트가 필요하다.Deduct 체크를 진행할 때 왜 두 번 검증할까?서비스 검증과 도메인 검증이 별개의 역할이라고 판단하여 두 번 진행하는 것이다.요구사항을 정확히 파악하고 테스트 케이스를 분리하는 것이 좋다고 생각했다.예외 케이스에 대한 테스트도 반드시 진행해야 한다.과제테스트 코드를 작성하는 것은 너무 생소하고 강의를 들었을때랑 내가 직접 작성하면서 했을때랑 떠오르는게 다르다 생각했다. 그리고 조그만한 테스트도 해야하나? 라는 고민도 많았고 기존에 했던 Readable보다 더 오랜 생각을 했었던 것같다. 테스트 코드를 작성 후 이후 강의에 대한 내용을 들어보고 나서 역시 모든 케이스는 커버를 해야된다라는 생각이 들었다.회사에서는 직접적으로 테스트 코드를 도입하지 않았지만 강의를 듣고 공부를 진행하면서 테스트 코드의 세분화를 잘해야할꺼같다.관련 코드 : https://github.com/backgom1/readable-code/tree/main/src/test/java/cleancode/minesweeper/tobe
2024. 10. 13.
0
[인프런 워밍업 스터디 클럽 2기_BE] 2주차 회고록 정리
주석은 죄악이다? vs 주석은 필요하다주석이 많다는 것은 요구사항을 코드에 잘못 녹여냈다는 이야기..!! 좋지 않다.코드를 설명하는 주석을 쓰면 코드가 아니라 주석에 의존한다.주석에 의존하여 코드를 작성하면 적절하지 않은 추상화 레벨을 갖게 되어 낮은 품질의 코드가 만들어진다.근데 주석은 언제쓰나요?좋은 주석 ✅ 우리가 가진 모든 표현 방법을 총동원해 코드에 의도를 녹여내고, 그럼에도 불구하고 전달해야 할 정보가 남았을 때 사용하는 주석 리팩토링을할때, 조상님들이 남긴 히스토리에 대해 알수없을때 코드를 작성할때 의사소통이나 코드로 표현할수 없을때 주석을 상세하게 설명A안과 B안을 통해 ~~~ 테스트를 결정된 사항에 대해 문서는 이런식으로 작성한다.주석을 작성할 때, 자주 변하는 정보는 최대한 지양해서 작성만약 관련 정책이 변경되었다면, 주석도 잊지 않고 함께 넣는다.1 , -1 , 0 이런거는 매직넘버로 생각할수있다. 그래서 Enum으로 가독성을 올려 추상화 레벨도 맞출수있는 효과가 있다! gameStatus == GameStatus.IN_PROGRESS; 강의중에 이런 내용도 추상화 레벨을 맞추기위해 메서드로 뺀것보고 정말 추상화레벨 이해가 정말 중요한거같다.checkIfGameIsOver(); Open을하고 종료 체크를 하는것이 추상화 레벨이 동일하다 판단해서Enum을 받아서 처리하면 좀 더 직관적으로 사용하기 좋다다양한 기법을 활용하면 새로운 도메인 지식을 얻을 수 있고 더 많은 생각을 할수있다.변수와 메서드의 나열순서변수는 사용하는 순서대로 나열한다.메서드의 순서도 고려해야하는데 객체의 입장을 항상 들어보자run이라는 메서드의 출현한 순서대로 배치actOnCell이라는 메서드의 순서에 대해 배치공개 메서드는 상단에 배치하는 것을 선호또 나눈 공개 메서드의 기준상태 변경 >> 판별 메서드 ≥ 조회 메서드 순서로 하는게 좋다.비공개 메서드는공개 메서드의 출현한 순서대로public 1private 1private 2이런식으로 배치하면 좋을꺼같다.나열 순서로도 의도와 정보를 전달할 수 있다는 것.메서드와 변수를 만들때 어디다 배치하지?이게 공개 메서드에서 내부 메서드로 의심을 해본다.변경 메서드여도 private면 우선순위가 낮아진다.내부에서 돌면 private로 바꿔준다.패키지 나누기패키지는 문맥으로써 정보를 제공할 수 있다.패키지는 쪼개지 않으면 관리가 어렵다.반대로 너무 잘개 쪼개면 관리가 어렵다.대규모 패키지 변경은 팀원과의 합의를 이룬시점에 하자.commit이 끝난후 작업을 하던 해야지 아니면 머지할때 고생한다..문과적으로 생각해서 보드는 셀과 포지션의 부모와 같은 상위 개념이라 옮겨도 된다.인터페이스와 구현부랑 나눠서 패키지구조를 잡는방법을 진행했다.기능 유지보수하기1. 버그잡기기능을 추가하거나 수정할때 좋은 방법깃발을 다꽂으면 게임이 승리조건으로 끝난다. → 버그케이스왜 이렇게 될까?로직적으로 코드가 문제가 되는곳이 있었으며, 이를 개선하기 위해 코드한줄한줄을 읽고 문제점을 파악하여 변경해나갔다. 코드를 읽으며 코드의 흐름 즉 추상화레벨을 잘 파악하는 것이 버그를 잡는 포인트다.2. 추상화 레벨을 맞추기생각해낸 방법추상화 레벨을 맞추기 위해서 한줄 한줄 코드를 읽고 분류를 진행한다.중복으로 뽑아낼수 있는 부분을 뽑아낸다.이미 타입을 선택했기때문에 타입으로 필터링으로 추출해도된다.중복된 로직에 대한 범위를 잡기부가적인 요소에 대한 내용을 메서드로 추출하기같은 메서드 동작임을 판단해서 생각하는것도 필요하다.null 객체 반환은 좋지 않아 Optional로 관리를 진행할 예정이다.파라미터에 Optional을 받으면 좋지 않은 안티패턴이다.한단위가 끝날때 , 조각조각 나눠서 커밋을 하는게 중요하다.객체의 책임과 응집도IO 통합일급 컬렉션display()의 책임Order 객체IO의 통합IO를 통합하여 같은 일련의 과정으로 묶여서 통합 객체를 만들어서 사용자의 객체하나로 한다. 왜 근데 그렇게 할까?일급 컬렉션일급 컬렉션을 활용하면 장점이 컬렉션의 가공이 일급 컬렉션에서 사용된다. 추상화되어일급 컬렉션을 활용하면 테스트 코드도 짜기 편하다. 매우null을 반환하는건 좋지 않다. 그러니까 Optional을 활용하여 null 객체를 처리를 꼭하자.display의 책임코드를 옮기고 둘이 동일한 로직을 타는것을 알수있다. 다른것은 파라미터뿐!이용권에 대한 인터페이스를 만들어서 관리하면 어떨까?필드가 공통적이라면 인터페이스를 만들어서 관리하는게 훨씬 좋다 상속은 별로다!!인터페이스를 만든다.해당 인터페이스를 가져온다.출력을 담당하는로직에서 이용권 계산은 도메인과 맞지 않고 올바르지 않다. 그렇기 때문에 분리하자.관점의 차이로 달라지는 추상화FineHandler를 바라보는 관점파일을 읽어오는 모든 행위에 대해서 가져오기때문에 무거워지기때문이다.구글시트나 다른 내용에 대해 유연성이 떨어진다. 그렇기 때문에 인터페이스에 대한 응집도가 올바르지 않다.제공을 해주는 접근, 읽는 접근으로해서io와 provider를 분리하는 이유는? 되게 중요하다.읽는다는 개념은 구현의 추상화 레벨이 낮고 , 제공해주는 개념이기때문에 따로 패키지에 스펙을 둔것이다.→ 헥사고날 아키텍처 - 포트와 어댑터의 기본이 되는 개념Provider과 다양한 역할을 보면서 클래스 작성에도 큰 책임 따른다 생각을 했다.능동적 읽기복잡하거나 엉망인 코드를 읽고 이해하려 할 때, 리팩토링하면서 읽기공백으로 단락 구분메서드와 객체로 추상화주석으로 이해한 내용 표기하며 읽기우리에게는 언제든 돌아갈 수 있는 git reset —hard 가있다.핵심 목표는 우리의 도메인 지식을 늘리는 것. 그리고 이전 작성자의 의도를 파악하는것오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링가능성이 낮아보이는 요구사항에 대해 리소스를 투자하는 행위은탄환은 없다.클린 코드도 은탄환이 아니다.실무 : 2가지 사이의 줄다리기지속 가능한 소프트웨어의 품질 vs 기술 부채를 안고 가는 빠른 결과물클린 코드를 추구하지만 코드 센스가 아주 중요하다. 클린 코드의 사고법을 기반으로 결정회사에서 많은 기법을 넣으려고 노력을 많이했지만 해당 강의를 들으니 많은 반성을 하게되었다. 꼭 좋은 기술 스택이여도 오히려 나에게 독이 될수 있다는 것을 느꼈다. 기술 스택을 넓히기보단 왜? 라는 중점을 두고 깊게 파고드는 개발 방향성을 잡아야겠다.2주차에 있는 day7의 과제중 객체지향적으로 코드를 설계하고 구현을 하는 내용이 있었다. 회사에서 어느정도 리팩토링을 진행해봤고 자신도 있었지만 코드를보니 머리가 하얘졌다. 첫번째로 실수한건 요구사항을 읽지않고 코드만 읽어 도메인 개념을 이해하고 개발을 진행하려했던것 코드한줄한줄을 분리하기 어려웠던 것 다양한것도 있었지만 요구사항을 파악하지 못했던 점이 가장 큰 페인이 되었던거같다. 요구사항을 재대로 보았으면 오래걸리지않고 코드를 더 빨리 구조화했을텐데 그러지못해서 많이 아쉬웠다. 다음번에는 요구사항에 대해 꼼꼼히 찾아봐야겠다또한 이번 중간평가에서 피드백을 받고 너무 많은 오버엔지니어링을 했다는 점에 반성을 했고 다른 사람들의 피드백을보며 나도 더 열심히해야겠다는 생각이 들었다 나보다 개발을 잘하는 사람은 널렸다. 겸손해야겠다.다음 강의에 대해 테스트 코드를 배우는 시간인데 너무 설렌다. 적용을 하고싶어도 구체적인 범위에 대해 어려움이 많았는데, 이번 강의에 대해 크게 얻어가는게 있었으면 좋겠다관련 코드 :https://github.com/backgom1/readable-code/tree/main/src/main/java/cleancode/studycafe/asis
2024. 10. 06.
0
[인프런 워밍업 스터디 클럽 2기_BE] 1주차 회고록 정리
오랜만에 인프런 워밍업 클럽을 진행했다.기존에 한 번 들었던 강의들이지만 너무 알차고 맛있는 강의였기 때문에 다시 듣는 마음에 설레였다.이번 주차에서는 가장 중요하다고 생각한 것들이 있는데추상과 구체논리, 사고의 흐름객체 지향 패러다임SOLID객체 지향 적용하기이 흐름이었는데, 정말로 강의를 보면서 느낀 것은생각하는 방법에 대해 알려주기 → 생각을 직접 해보기 → 생각한 것에 대한 구현을 진행하기라는 순서로 이루어져 있다는 것을 깨달았다.추상화를 잘하는 것은 핵심을 파악하는 것이며, 분석하는 능력을 키울 수 있다고 생각했다!첫 주차 과제를 진행하며, 추상과 구체를 작성하는 방법을 배웠고 실생활에서 구체화를 하는 작업을 해보았다.이름이랑 메서드명, 심지어 띄어쓰기를 하는 것조차 추상화의 과정이라는 점이 너무 신기했다.추상화 레벨이라는 내용도 있었는데, 상당히 많은 도움이 된 것 같았다. 코드를 읽으면서 "이건 구체화된 내용이지 않을까? 가독성이 좋은가?"라는 생각을 끊임없이 이어갔다.내가 부족했던 부분은 핵심을 파악하고 간추리는 생각이었는데, 생각하는 방법을 기르는 연습을 한 것이 너무 좋았다. 구현은 어떻게든 할 수 있지만, 어떤 방식으로 설계할지는 나의 몫이라고 생각했다!생각한 후에는 그에 맞는 객체를 설계해야 하는데,설계 과정에서 주의해야 할 점이 머릿속에 박혔다. "무례한 행동"이라는 말이 머릿속에 박혀 토이 프로젝트나 개발을 진행하면서 getter나 setter를 사용하려고 할 때 많이 떠올랐다.객체를 설계할 때 높은 응집도와 낮은 결합도가 핵심이었다. 용어를 알고 개발하는 것과 모르고 하는 것의 차이가 크다는 것을 알았다.응집도: 하나의 클래스가 기능에 집중하기 위한 모든 정보와 역할 결합도: 모듈 간의 상호 의존성을 나타내는 개념객체의 결합도와 응집도에 대한 이해하기 쉬운 정리점진적인 리팩토링 방법도 배우고 정말 꿀팁만 얻은 느낌이다.도메인 지식은 생각하는 것이 아닌 발견하는 것이다. 도메인 주도 개발을 공부할 때, 도메인 지식을 어떻게든 생각해서 개발을 진행했는데, 하다 보면 도메인이 발견되고 그에 맞게 개발을 하며 점진적인 리팩토링을 하는 궁극적인 개발 방법을 배운 것 같다.SOLID 원칙에 대해 이론적으로만 공부했었는데, 직접 코드를 작성하며 배우니까 확 와닿았다.단일 책임 원칙 (SRP): 하나의 클래스는 단 한 가지의 변경 이유만을 가져야 한다.개방-폐쇄 원칙 (OCP): 확장에는 열려있고, 수정에는 닫혀 있어야 한다.리스코프 치환 원칙 (LSP): 상속 구조에서 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.인터페이스 분리 원칙 (ISP): 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.의존성 역전 원칙 (DIP): 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.전체적으로는 이전에 배운 책임과 역할에 대해 이론적으로 가장 많이 정리된 느낌이었다. 그중에서 SRP와 DIP가 가장 와닿았으며, 책임과 역할별로 클래스를 나누고 추상화된 인터페이스를 참조하여 사용하는 것을 직접 코드로 작성하며 배웠다. 기존에 작성했던 코드에 대해 많은 반성을 하게 되었다. 책임이 없는 나쁜 코드였던 것 같다.섹션 5에서 직접 객체 지향을 코드에 적용해 보았다.각 내용을 정리해보면,Entity는 식별자를 통해 고유성을 가지며, 시간에 따라 상태가 변할 수 있지만, VO는 불변성을 가지며 모든 속성이 동일하면 같은 객체로 간주된다.일급 컬렉션은 컬렉션을 감싸는 클래스로, 컬렉션을 포함한 비즈니스 로직과 불변성을 보장하여 관리하는 객체이다.Enum은 상수의 집합이며, 상수와 관련된 로직을 담을 수 있는 공간이다.다형성 활용하기 부분에서는 전략 패턴과 함께 적용해서 회사에서도 코드 작성을 더 깔끔하게 해 보았다. 아직 일급 컬렉션에 대해서는 확실히 와닿지는 않아서 추후에 공부하면서 "아, 이런 개념이 있었구나"라고 언젠가 깨달을 것 같다.2주차 과제는 주어진 메서드에 대해 객체를 설계하고 적용해 보는 내용이었다. 섹션을 보고 큰 키워드를 떠올리며 개선을 했는데, 딱딱 맞아떨어지면서 "아, 이렇게 다음에 큰 규모의 코드가 있을 때 바꾸면 되겠구나"라고 어느 정도 감을 잡았던 것 같다.관련 코드: GitHub 링크아직 객체 지향에 대해 공부를 많이 하지는 않았고 익숙하지는 않지만, 조금씩이라도 이런 식으로 하면 좋겠다는 생각을 한 것 자체로 성공이라고 생각한다. 2주차를 진행하면서 더 확 와닿을 수 있기를 기대하며 계속 공부를 이어가야겠다.
2024. 03. 09.
0
[인프런 워밍업 스터디 클럽 0기_BE] 3주차 회고록 정리
요일 별 내용 정리Day11배포와 Profile 사용법예전에 배포를 수행하며, 막연하게 AWS를 사용하여 배포를 진행했지만 보안이나 기능적인 측면에서는 고려하지 않고 진행을 했었다.내가 작성한 코드를 작성하며, 하나의 기본 셋팅을 가지고 개발을 진행하고 있었는데, Profile이라는 개념을 알고 적용을 해보았습니다. h2 DB를 메모리에 저장하면 휘발되며, 개발단계에서 사용하는 것을 알았습니다.예전에 테스트 코드를 작성하며 작업을 진행할 때, h2 DB를 활용하여 테스트 마다 초기화를 진행해주면 편하다 생각을 했습니다.spring: config: activate: on-profile: local //... 설정 값on-profile값에 따른 하위에 작성된 설정한 값으로 적용 되도록 합니다. datasource: username: "sa" password: "" url: "jdbc:h2:mem:library:MODE=MYSQL;NON_KEYWORDS=USER" driver-class-name: org.h2.Driver메모리 모드로 사용하며 MySQL 방언을 사용하며 USER의 예약어를 사용하지 않는다. 라는 설정입니다. h2: console: enabled: true # path: /h2-console #h2 Console을 접속을 가능하게하며 , Console에 대한 경로 값 입니다.부트에서 실행할 때 ActiveProfiles 설정 값을 내가 작성한 프로파일명으로 실행됩니다.JUnit을 활용할때는 @ActiveProfiles("프로파일명") 어노테이션을 활용하여 데이터베이스 환경을 변경할 수 있습니다. 테스트 코드를 작성하고 실행할때, 데이터베이스에 대한 정보가 부담스러워 다른 방법이 없었나 찾아봤는데 Profile이라는 기능을 활용하여 테스트에 대해 원활하게 작업을 할수 있다 생각하여 빨리 활용하고싶다는 생각이 들었습니다. git과 github깃허브는 예전에 취업하기전에 포트폴리오를 올려 사용했었고, 취준생일때 git과 github는 같은것이라 생각했는데, 강의에서 나왔듯이 아예 다른 내용인걸 듣고 아직 많이 부족하다 느꼈습니다. 또한 github를 저는 클라우드 형식으로만 사용했는데 다양한 기능을 배울 수 있어 설레는 마음으로 강의를 시작했습니다!git이란? : 버전을 손쉽게 관리를 하도록하는 버전 관리 프로그램입니다.github란? : git으로 관리되는 프로젝트의 코드가 저장되는 저장소저는 이미 깃허브를 사용해서 아이디를 생성하지 않았습니다 .. !!!강의를 진행하기전에 library-app을 이미 푸쉬 작업을 하고있었고, 그 내용을 다시 복습하는 내용으로 강의를 들었습니다.!!하지만 sourcetree를 사용해보았고, 직접 터미널에서 깃을 사용해본적은 없었습니다.git init : 로컬 코드 깃 초기화기존에 올렸던 프로젝트에서 초기화 하니까 다 빨간색으로 반영이 되지않아 매우 놀랐습니다..하지만 강의 순서대로 따라갔더니 정상적으로 되었고 안심을 했습니다.. 깃은 정말 잘 쓰고 활용해야할꺼같습니다.git remote add orign "프로젝트 경로" : 깃 프로젝트 경로 연결git add . : 작성한 내용에 대한 전부를 깃에 반영하겠다.git commit -m : 커밋을 진행git push : 깃허브에 반영이 되도록 올려주는 명령어 또한.. AWS도 예전에 포트폴리오를 사용하고 배포할때 사용해본 경험이 있었습니다. 진짜 기본기를 쌓는다는 마음으로 가볍게 훑고나니 정말 유익했었습니다. Day12리눅스 명령어 정리chmod 400 :ssh -i : 다른 쉘에 접근하기 위한 프로토콜을 의미mkdir : 폴더를 만드는 명령어ls : 현재 리스트 정보ls -l : 리스트 세부 내용까지drwxrwxr-x : 폴더인지 / 폴더소유자권한owner /폴더 소유그룹 권한group / 아무나 접근할때 권한otherls -a : 숨김파일까지 표시cd : 폴더 이동cd .. : 하위폴더로 이동rmdir : 비어있는 폴더 삭제sudo yum update : 리눅스 관리 프로그램 최신화입니다. 저는 우분투를 사용해서 sudo apt update를 사용했습니다.sudo yum install git : 깃허브 패키지 다운로드sudo yum install java-11-amazon-corretto -y : 자바11버전 jdk설치java -version : 자바버전확인 -> 잘 깔렸는지 확인!!sudo yum install mysql-community-server -y : mysql 설치 !!sudo systemctl status mysqld : mysql 상태 확인 !!sudo systemctl restart mysqld : mysql 재실행 명령어mysql -u root -p : 임시 비밀번호를 알아오는 명령어 예전에 jar파일을 실행하다 렉이 걸려서 멈춘 경험이 있었고 인스턴스 종료를 눌렀는데, 그게 삭제인줄 모르고 가상머신을 날린 기억이 있었다.. 강의를 중간에 보다보니 메모리 대신 disk의 일부를 사용할 수 있도록 하는 명령어를 보고 아 저렇게 하면 메모리를 더 쓸수 있겠구나라고 생각했습니다.sudo dd if=/dev/zero of=/swapfile bs=128M count=16sudo chmod 600 /swapfilesudo mkswap /swapfilesudo swapon /swapfilesudo swapon -s ./gradlew build : 프로젝트를 빌드./gradlew build -x test : 프로젝트를 빌드하는데, 테스트는 생략./gradlew clean : 현재 빌드된 결과물을 제거저는 jar로 파일을 빌드한 후 실행을 진행했는데 직접 리눅스 환경에서 빌드를 진행하고 실행까지 하는것도 있구나라는 방법이 있었고, 나중에 중단배포나 jenkins를 활용하여 작업을 하려고 선행으로 공부했는지는 찾아봐야할꺼같다 생각했습니다!!foreground와 background의 차이를 공부하여 서버를 종료할 때 같이 종료가 되지않도록 배웠습니다.nohup java -jar build/libs/"서버명".jar --spring.profiles.active=dev &을 활용해 백그라운드 실행을 하면됩니다.kill -9 프로세스번호 : 해당 프로세스를 종료하는 명령어vi nohup.out : 편집기로 로그 실행저는 less를 사용하여 작업을 많이 진행했습니다. 리눅스 명령어를 활용해보면서 정말 많이 몰랐고 데이터베이스를 가상머신에 직접깔아서 사용해보았습니다. 저는 아마존 rds를 활용하여 데이터베이스 서비스를 사용했는데 직접 사용도 해보니까 좋았다 생각했습니다. 하지만 같은 가상머신에 존재하면 서버가 죽었을 경우 데이터베이스도 죽을 경우있다 생각했는데 그게 맞는지 잘 모르겠습니다. 또한 GUI 프로그램을 활용하여 개발을 진행해서 기본기가 부족하다 많이 느꼈습니다..저는 우분투로 사용했는데 아마존 리눅스를 많이 사용하는걸 코치님의 강의를 보고 처음알았습니다.. 우분투 말고 다음 토이 프로젝트를 진행할 때, 아마존 리눅스를 활용하여 개발을 진행해봐야겠다는 생각을 했습니다.!!!! Day13plugins { id 'java' id 'org.springframework.boot' version '3.2.2' id 'io.spring.dependency-management' version '1.1.4' }제가 사용하고 있는 버전입니다 java를 사용하고 springboot 3.2.2를 사용하며 패키지 의존성 매니저는 1.1.4버전을 사용하고있습니다.group = 'com.group' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '17' }group : 프로젝트 그룹을 의미version : 프로젝트 버전을 의미sourceCompatibility : 프로젝트가 사용하면 JDK버전을 의미합니다.dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-aop' }라이브러리/프레임워크를 표시하는 곳implementaion : 의존성을 가져와 항시 사용 runtimeOnly : 코드 실행할때만 사용!왜 저렇게 나눴는지 찾아보니 빌드를 진행할때, 성능적인 이점을 얻을 수 있도록 구분을 합니다라고 나와있어 구분을 하려는 이유를 알았습니다.!! 스프링과 스프링부트 차이이건 예전부터 너무 궁금해서 많은 사람들에게 물어봤고, 어떤 차이가 있는지 명확하게 몰랐습니다. 예전에 친구랑 프로젝트를 진행하며 스프링과 스프링부트가 뭔차이냐!! 라고하며 기존 방식을 고수했던 경험이있었는데 솔직히 그때 굳이 나누는 의미가 있었냐라고 웃기도 했습니다 ㅎㅎ... 근데 강의를 듣기전에 내용을 보며 확실히 알아가자 생각했습니다.SpringBoot란? Spring Framework에서 사용하는 기능을 편하게 사용하도록 자동으로 설정해주는 프레임워크입니다.간편한 설정간단한 의존성 관리강력한 확장성MSA의 모니터 링yml과 properties , lombokYAML의 문법spring: config: activate: on-profile: dev #spring: 계층은 들여쓰기로 나눕니다. 주석은 #입니다.회사에서는 properties를 활용하는데 관리하기가 불편하면서도 편한거같다 생각했습니다. yml구조로 설정을 하면 가독성이 좋아져 딱딱 나눠져 있어서 좋았습니다.그리고 롬복을 예전부터 많이 사용하여 손에 많이 익었으며,내가 알고있던 내용과 부족했던 내용 새로운 내용을 공부하게 되어 너무너무 좋았습니다.그리구.. 마이그레이션쪽은 이미 3.0을 사용하고있어 제 코드에 직접적으로 사용해보진 못했지만 이런 내용이 있고 다음에 활용할 수 있다 생각했습니다 실무에서나 스프링부트 4.0이 나온다면 강의를 들으면서 점점 스터디가 끝난다는 생각에 아쉽다 생각했습니다..ㅜ 그래도 마무리가 아름다워야 좋다 생각하여 끝까지 열심히 들을 생각입니다!!! Day14드디어 강의 마무리이며, 마무리 영상을 보면서 내용을 정리하는 시간을 가지려했는데 엄청 도움이 되는말들이 많아 좋았습니다.개발하면서 방향성이 너무 많아 어려웠는데 마무리 강의보면서 방향성을 다시 잡을 수 있었습니다. OOP나 디자인패턴의 실무 스킬을 배워야 스프링의 원리를 알아갈수 있다.스프링에 대한 모듈은 인프라나 보안 이론지식이 필요하다.기반 실무 스킬에 대한 내용이 있었고, 필수적인 내용이라 생각하여 꾸준히 공부를 해나가야 생각했습니다!프레임워크를 배제하고 내 기본기라 생각하면 될꺼라 생각했다.IT 기술 블로그중 신입 개발자, 파일럿 프로젝트 관련 글을 읽기.다른 사람은 어떤방식으로 코드를 작성했고, 왜 그렇게 작성했는지 시간이 된다면, 코틀린을 공부해보자.그리고.. 인스턴스 종료에 대해 나왔는데.. 얼마전에 프로젝트를 진행하며, 종료버튼을 누르는게 삭제인지.. 모르고 삭제된경우가있었다.. 주의를 해야겠다 생각했습니다.. 추가강의도 너무 많이 도움이 됐습니다.미니프로젝트미니프로젝트는... 시간만 많았다면.. 정말 설계부터 완벽하게 설계한다음 진행했어야했는데 저 맛있는 내용을 너무 소화하지못하고 마무리된거같아 아쉬웠습니다. 해당 내용은 스터디가 끝나더라도 마무리를 꼭 지어 끝내야겠다 생각했습니다.팀원분들과 같이 코드리뷰를 진행하며, 모르는내용에 대해 많은 것을 배웠고 행복했던시간이였던거같습니다.설계만 작성하고 그 단계별 개발 진행 사항은 작성하지 못한것이 아쉽습니다. 이또한 천천히 마무리를 진행할 예정입니다.notion : https://silvercastle.notion.site/51409fcd18644974819dc23cdc2e902b?pvs=4github: https://github.com/backgom1/Inflearn_BE0_Study_mini/tree/develop/backgom1/ensmini라이브강의에서 코드리뷰를 진행해주시는 내용이 있었는데 딱딱 제가 부족한것을 찝어줘서 너무 시원했습니다 ㅋㅋㅋ!!도메인계층에 대해 다시한번 생각을 하게 된 계기가 있었고, 강의나 책을 읽어서 공부를 본격적으로 해봐야겠다 생각을 했습니다.
2024. 03. 03.
0
[인프런 워밍업 스터디 클럽 0기_BE] 2주차 회고록 정리
요일 별 내용 정리Day6 강의스프링 컨테이너란?개발자 대신 객체의 생명 주기를 관리해주는 곳입니다.스프링 빈이란?스프링 컨테이너 안에 인스턴스를 넣어주는 것이 스프링 빈이다. 따라서 빈으로 등록된 메서드들은 스프링 컨테이너가생명 주기를 관리합니다.빈 등록 방법에는 @Bean을 붙혀줘야하며, 항상 클래스에 @Configuration을 선언해줘야 가능합니다.여기서 @Configuration 의 사용은 외부 라이브러리, 프레임워크에서 만든 클래스를 등록할때 사용됩니다.@Component를 사용하여 빈 등록이 가능합니다.주어진 클래스를 컴포넌트로 확인하며 해당 클래스는 서버가 런타임 시점에 자동으로 감지됩니다.강의를 공부하며 배웠던 @Repository,@Service,@RestController에 대한 어노테이션 내부에 @Component어노테이션이 달려있습니다. 주로 사용할 때는 개발자가 직접 작성한 클래스에 사용되며 @Bean어노테이션이 생략됩니다.여기서 더 나아가 @Configuration vs @Component다양한 빈 주입방식주입 방식 중에 자동으로 주입을 받는 어노테이션이 있는데 그건 바로 @Autowired입니다.자동으로 스프링에 의존성을 주입하는 역할을 수행합니다.생성자를 이용해 주입 받는 방식 private final BookJpaRepository bookJpaRepository; private final UserLoanHistoryRepository userLoanHistoryRepository; private final UserRepository userRepository; @Autowired public BookService(BookJpaRepository bookJpaRepository, UserLoanHistoryRepository userLoanHistoryRepository, UserRepository userRepository) { this.bookJpaRepository = bookJpaRepository; this.userLoanHistoryRepository = userLoanHistoryRepository; this.userRepository = userRepository; }생성자를 통해 주입 받는 방식으로 가장 권장하는 방법입니다. @RequiredArgsConstructor를 사용하여 생성자를 생략할 수 있습니다. 여기서 @RequiredArgsConstructor은 롬복이 정의해놓은 어노테이션으로 final이나 notnull로 되어있는 생성자에 대한 내용을 자동으로 생성해주는 어노테이션입니다.Setter를 이용한 주입 방식private JdbcTemplate jdbcTemplate; @Autowired public void setJdbcTemplate(JdbcTemplate jdbcTemplate){ this.jdbcTemplate = jdbcTemplate }Setter에 의존성 주입을 하는 방법이지만 사용하지 않습니다.필드에 직접적인 주입 방식@Autowired private JdbcTemplate jdbcTemplate;필드에 직접적으로 의존성 주입을 직접거는 방식이지만, Field injection is not recommended이라는 프레임워크 메세지와 함께 사용되지않는 방법입니다.강의에 대한 일부 내용으로 인터페이스를 생성하여 다형성을 활용해 메서드를 나눠놓았지만, 문제점은 어떤 곳에 대한 클래스를 사용해야할지 모르는 상황이 올 수 있는데 두가지 방법이 있습니다. @Primary클래스 위에 어노테이션을 사용하면 스프링 컨테이너에 있는 빈에 대한 우선권을 주는 어노테이션입니다.@Qualifier많은 후보군 중에서 어떤 것을 스프링 컨테이너에 넣어주는 역할을 하는 어노테이션입니다.클래스에서 소문자로 줄여서 사용합니다. appleService 또한 각 클래스내에 @Qualifier(”지정한 이름”)를 사용하면 양쪽 모두 사용하면 @Qualifier끼리 연결이 된다.@Primary, @Qualifier 우선 순위@Qualifier를 직접 작성한 클래스에 대해 우선 순위를 가진다.스프링은 사용자가 직접 명시한것이 우선순위가 높다. 과제노션 : https://silvercastle.notion.site/6-92b90a65eb4a4cfaa7241162fd8e3fa2?pvs=4깃허브 : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/daliy/daysix Day7강의JPA란?JPA(Java Persistence API)는 자바 진영의 ORM의 표준 기술ORM이란?객체와 관계형 데이터베이스를 짝지어 데이터를 영구적으로 저장할 수 있도록 정해진 Java진형 규칙Hibernate란?자바 진영의 다양한 ORM 프레임워크 중 가장 많이 사용되는 프레임워크 Hibernate로 만들어진 ORM 표준 기술은 JPAJPA를 사용하기 위한 설정 jpa: hibernate: ddl-auto: none properties: hibernate: format_sql: show_sql: dialect: org.hibernate.dialect.MySQL8Dialectddl-auto : 스프링이 시작할때 DB 테이블 동작을 진행합니다.create : 컴파일 시작할때 기존 테이블이 있어도 삭제 후 새로생성create-drop : 런타임 종료후 dropupdate : 객체와 테이블이 다른 부분만 변경validate : 객체와 테이블이 동일한지 확인none : 아무 동작xcreate,update는 개발서버에서 사용하며, 운영서버에서는 사용하지 않고 none과 validate는 운영서버 및 개발서버에서 자주 사용할꺼같다 생각이 들었습니다.show_sql: JPA에 대한 쿼리를 보여주는 설정format_sql: 쿼리에 대한 동작에 정렬 설정dialect: 데이터베이스에 따른 방언을 맞추기 위한 설정 JPA에 대한 다양한 어노테이션 @Entity : 테이블과 객체가 1:1로 대응하는 데이터를 이야기합니다.왜 기본 생성자가 없는것을 entity에 작성할까? JPA는 Reflection API를 사용하여 객체를 생성하기 때문에 기본 생성자가 필요 private은 지연로딩 설정시 Proxy 오류가 발생합니다.Reflection API : 리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API@Column@Column(nullable = false , length = 20, name = "name")null확인, 길이, 이름을 지정합니다. 테이블의 컬럼을 지정하는 어노테이션입니다.name이 동일하다면 name은 생략하다. Column 어노테이션도 생략이 가능하다. JPA의 쿼리 작성 방법public interface UserRepository extends JpaRepository { }레포지토리 인터페이스를 생성하여 JpaRepository를 입력해주면 Spring Data JPA 사용이 완료 됩니다. private final UserRepository userRepository; public void saveUser(UserCreateRequestDto request) { userRepository.save(new User(request.getName(), request.getAge())); }생성자를 이용한 의존성 주입을 진행한 후 쿼리문을 날려 사용합니다. save는 이때 엔티티의 있는 필드명을 입력을 해줘야합니다.기본적으로 제공하는 Spring JPA 메서드save: 사용자를 생성,수정을 하는 메서드findById: 아이디를 통해 가져오는 메서드 Optional, 타입findAll: 전체 조회 반환은 Listexist : 쿼리 결과가 존재하는지 booleancount : SQL의 개수를 센다.Optional을 활용하여, null값에 대한 처리를 진행하여 코드에 대한 가독성 및 null처리를 유연하게 처리할 수 있습니다.과제해당 과제를 진행하면서 Stream문에 대한 사용방법과 도메인 객체를 만들면서 아 이땐 이렇게 했구나라는 대답을 얻을 수 있었습니다. 또한 왜 사용해야지 하면서 깊게깊게 공부하다보니 이런식으로 동작하는 구나에 대해 많이 배웠습니다.또한 클래스와 엔드포인트가 동일한 문제를 겪으면서 프로젝트에 대한 패키지에 대해 다시 되돌아보는 시간이였습니다.코치님이 말씀해주신 enum을 통해 분기처리를 작업하는 방향성에 대해 생각하지 못했는데 알게 되어 기뻤습니다.노션 : https://silvercastle.notion.site/7-28ba1cb6c81a4972b6ec38a4cd69ef96?pvs=4깃허브 : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/daliy/dayseven Day 8강의트랜잭션이란?데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위 , 쪼갤 수 없는 업무의 최소 단위강의 내용 중에 쇼핑몰 사이트에 주문을 하기 위해 주문작업에 대한 내용을 쪼개보면, 주문기록저장,포인트저장,결제기록 저장을 진행해야하는데 주문기록저장,포인트저장 작업이 정상적으로 수행되어도 결제기록 저장에 예외가 발생하면 다같이 처리를 해주지 않아야하는데 주문기록저장,포인트저장은 저장이 되는 상황을 막기위해 쿼리동작을 한번에 처리하는 것을 의미하기도합니다.SQL에서 사용하는 트랜잭션 사용방법start transaction; : SQL에서 트랜잭션 시작commit : 트랜잭션 실행rollback: 트랜잭션 실행 실패만약 다른 접속 사용자들이 insert문을 작업을 진행하였고, 또 다른 접속 사용자가 select를 해도 commit이 되어있지 않기 때문에 다른 접속자의 저장된 내용을 확인 할 수 없다. 강의에서 트랜잭션 적용해보기//어노테이션을 달면 메서드 시작 아래에서 트랜잭션 시작 //함수가 예외없이 잘끝나면 commit //아니면 rollback; @Transactional public void saveUser(UserCreateRequestDto request) { userRepository.save(new User(request.getName(), request.getAge())); throw new IllegalArgumentException("틀렸습니다."); }트랜잭션 어노테이션을 통해 saveUser라는 곳에 트랜잭션이 시작되고, save를 진행하여 커밋을 대기하는 상황에 예외가 발생하여 rollback이 되어 데이터베이스상에 저장이 되지 않는 모습을 볼 수 있습니다. @Transactional(readOnly = true) public List getUsers() { List users = userRepository.findAll(); return users.stream() .map(user -> new UserResponseDto(user.getId(), user.getName(), user.getAge())) .collect(Collectors.toList()); }select절은 @Transactional의 속성 중 readonly = true는 생성,수정,삭제에 대한 처리를 진행하지않고 조회만 해 성능 향상에 도움이 됩니다!!여기서 알아야할점은 checkedException은 예외가 발생하여도 트랜잭션 작업에 롤백 상황이 있어도 커밋이 됩니다.하지만 구글링을 통해 확인 해보았는데! try-catch및 개발자가 직접적으로 예외를 던져준다면, checkedException도 롤백을 진행할 수 있습니다. 꼭 checkedException은 트랜잭션 작업에 롤백이 안된다는 무조건적인것이 아니였습니다!!영속성 컨텍스트란?테이블과 매핑된 Entity객체를 관리/보관하는 역할트랜잭션을 사용하면 영속성컨텍스트가 생성되고 , 트랜잭션을 종료하면 영속성컨텍스트가 종료된다.Entity를 저장하거나 업데이트를 진행 하는 변경을 감지해 자동으로 저장된다. → 변경 감지쓰기지연 : INSERT/UPDATE/DELETE SQL 을 바로 날리는것이 아니라 commit이 진행되면 한번에 날려서 보낸다. 왜 한번만 날릴까? 한번에 묶어서 하면 성능이 올라간다1차캐싱 : id를 기준으로 entity를 기본적으로 사용할 수 있도록함Day 9강의책 생성 API 개발예전 강의에서 배운대로 클래스를 작성했고, 레이어드 아키텍쳐를 통해 Controller,Service,Repository에 패키지 구조를 나눴습니다.엔티티객체를 생성했고, DTO를 구간을 나눠 작성하는 방법과 Spring Data JPA를 통해 저장하는 방법을 복습했습니다.책 대출 기능 개발하기user_loan_history라는 테이블을 생성하며, id값을 참조하고있고, tinyint를통해 boolean을 알아 성능에 대해 줄일수 있다는 점을 배웠습니다.기존과 동일하게 DTO , 계층과 JPA를 사용하여 기능을 개발했습니다.반납 기능 개발하기반납기능 강의중에 DTO를 재활용하는 것과 새로 만드는것에 대해 이야기가 나왔는데두 기능에 한 기능에 변화가 생기면 유연하고 사이드 이펙트에 대해 대처를 할 수 없다 생각했습니다.회사코드 및 개인 프로젝트에서는 하나로 묶은 만능 DTO를 만들었는데 각각 나눠서 관리하는게 더 낫다 생각하여 이후 작업에 진행할 예정입니다.저는 지금도 좋은 코드를 작성한 줄 알았고, 지금도 그런 방식으로 코드를 구현해왔지만 더 좋은 방법이 있다. 라는 내용과 함께 많이 설레기도 했습니다. 정말 강의를 들으면서 기본기와 심화적인 디테일내용을 잡을 수 있어 너무너무 좋았던 것 같습니다. Day10강의코드를 객체지향 적으로 짤 수 없는 강의를 들으며, 예전 코드 및 지금 코드에서 쿼리 저장할때 각각 저장하는 방식을 사용했는데, 도메인 계층을 하나의 관심사로 묶어 처리하는 과정을 배웠습니다. 예전에 코치님께 여쭤본 쿼리를 나눠서 보내는 과정에 대해 아! 이때 이런식으로 쿼리를 만들면 되겠구나. 라는 것을 깨달았습니다(아닐 수도 있습니다 하하..)또한 다양한 연관관계에 대해 배웠는데. @ManyToOne private User user;JPA는 연관관계에 대해 사용하는 방법으로 @ManyToOne으로 묶어 사용했으며, 반대 방향에서는@OneToMany(mappedBy = "user") private List userLoanHistoryList = new ArrayList();를 사용하여 양방향 연관관계에 대해 배웠습니다. 연관관계에 대한 주인은 주도권이 가지고있는 연결된 필드를 가진 사람이 주인이며mappedBy는 주인이 아닌 곳에 달아줘야하는것을 알았습니다. 또한 @ManyToOne를 가진 쪽이 주인인것으로 배웠으며, 연관관계에 많이 헷갈렸고 지금도 많이 헷갈렸지만, 조금씩 알아갔습니다.또한 주인의 대한 setter를 사용해야 연관되어있는 엔티티에 값이 저장되며, 주인이 아닌곳에 저장시, 주인 엔티티가 저장이 되지 않는 것을 배웠습니다. 다양한 연관관계 종류 @JoinColumn : 연관관계 주인이 활용할 수 있는 어노테이션필드의 이름이나 null여부 , 유일성 여부 , 업데이트 여부등을 지정@OneToOne : 1대1로 연관관계를 맺는 방식입니다. 하나의 사용자는 하나의 집 주소를 가지고 있습니다!@ManyToMany : 구조가 복잡하여 사용하지 않은것을 추천합니다. 중간 테이블을 만들어 관리를 진행해봤고 이것이 더 좋은 방법이라 생각이 됩니다!cascade : 함께 연결되어 있는 객체도 함께 저장되거나 삭제 되는 기능!만약 user와 대출기록이 묶여있다면 데이터베이스에 user와 대출기록이 삭제된다.orphanRemoval : 대출 기록이 삭제되어도 user는 삭제가 되지않는다. 객체간의 관계가 끊어진 데이터를 자동으로 제거하는 옵션 public void loanBook(String bookName) { this.userLoanHistoryList.add(new UserLoanHistory(this, bookName)); } public void returnBook(String bookName){ UserLoanHistory targetHistory = this.userLoanHistoryList.stream() .filter(history -> history.getBookName().equals(bookName)) .findFirst() .orElseThrow(IllegalArgumentException::new); targetHistory.doReturn(); }도메인 객체에 대한 관심사를 묶어 협력하여 도메인 계층에 비지니스로직이 들어간 코드를 보고 와.. 저렇게 할수도 있구나 이러면 기능적으로 명확해지겠구나라는 생각을 했고 연관관계에 대해 조금더 깊게 생각이 들었습니다.그렇지만 연관관계에 대해 많이 묶여버린다면 사이드 이펙트에대한 고려와 성능상의 문제가 생길 수 있어 여러 아키텍쳐 공부 및 비지니스 요구사항에 대해 공부를 해야한다 느꼈으며, 아 아직 정말 많이 멀었다 생각했습니다. 그렇지만 해야할 일을 하나씩 진행하며 성장하고 있다는 기분을 받았고, 그것이 원동력을 만들어주었던 것 같습니다. 2주차에는 급하게 작성한 부분들때문에 부족해보이고 모자랐다 생각이 들었고, 다음 주차에서는 좀 더 신경을 많이 써서 진행을 해야겠다 생각했습니다.또한 미니 프로젝트를 팀 별로 구성하여 작업을 진행해보며, 내가 미쳐 생각하지 못했던 부분, 내가 더 나은부분에 대해 토론을 하며 배워갈 예정입니다.
2024. 02. 24.
0
[인프런 워밍업 스터디 클럽 0기_BE] 1주차 회고록 정리
주차 별 내용 간단 정리 Day1강의서버에 대한 내용과 네트워크에 대한 개념적인 내용을 배웠습니다.네트워크 : 두대 이상의 컴퓨터를 연결하고 통신도메인 : 인터넷에 연결된 컴퓨터를 사람이 쉽게 기억할 수 있도록 만든 주소 라이브러리와 프레임워크 차이에 대한 내용라이브러리 : 프로그래밍을 개발할 때 필요한 구조나 기능들을 모아두고 개발자가 직접 코드 구현하는 것프레임워크 : 개발에 필요한 것들을 미리 구현 후 필요시에 꺼내 사용하는 것HTTP API에 대한 내용과 설명내용 정리API : 정해진 약속이나 규칙에 따라 특정 기능을 수행URL : 인터넷 상의 자원을 찾기 위한 주소 ( 프로토콜 / 구분기호 / 도메인 이름, 포트 -> IP는 도메인 이름으로 대체 가능 / 자원경로 / 쿼리 스트링 )HTTP 요청 메서드GET : 데이터 조회시 자주 사용하며, 쿼리 스트링을 통해 정보를 전송POST : 데이터 저장시 자주 사용하며, Body에 담아 요청 정보 전송PUT : 데이터를 수정할 때 , PUT이외에도 PATCH방식도 있음DELETE : 데이터 삭제 요청을 보낼 때 사용Client와 ServerClient : 요청을 보내주는 사람Server : 요청받은 요청에 따라 필요한 응답을 내려주는 사람GET /portion?color=red&count=2 Host: spring.com:3000GET방식으로 PATH는 /portion 쿼리 스트링은 color : red , count : 2서버는 spring.com:3000파라미터 보내는 방식은 (키,값) 으로 되어있습니다. @Getter public class CalculatorAddRequest { private final int number1; private final int number2; public CalculatorAddRequest(int number1, int number2) { this.number1 = number1; this.number2 = number2; } }DTO객체를 생성하여 값을 받는 것을 확인했었습니다. 평소에 DTO 객체를 사용할때 요청,응답에 대한 DTO객체를 하나로 묶어서 사용을 했었지만 강의를 보고 DTO 객체는 요청과 응답에따라 각각 객체를 생성해주는 것이 좋다 생각했습니다.또한 Setter함수는 사용하는 것을 지양하기때문에 생성자로 값을 받아오는 작업을 진행했습니다. @RestController public class CalculatorController { @GetMapping("/add") public int addTwoNumbers(CalculatorAddRequest request){ return request.getNumber1()+request.getNumber2(); } }@RestController : 해당 클래스를 API 진입지점으로 만들어주는 어노테이션 컨트롤러@GetMapping : GET방식 요청 정보를 받는 어노테이션@RequestParam : 요청된 쿼리 스트링에 대한 파라미터 미션첫번째 미션 접근 방식은 어노테이션 사용 이유와 나만의 어노테이션을 작성하는 것이였습니다.어노테이션에 대한 내용을 파악하고 코드로 사용을 사용했지만 직접 만들어본적은 없기때문에 많이 헷갈렸던거 같습니다.또한 어노테이션을 사용하는 이유에 대해 정확하게 정의를 내리지 못했지만 1주차 과제를 진행하며 많이 얻어갔던 것 같습니다. 나한테 들리는 왜?라는 질문에 대한 대답을 하였던 것 같아 재밌었던거 같습니다.한가지로 아쉬웠던 점으로는 문서 작성에 대한 미숙함으로 인해 두서없이 작성되는 것같아 다음에 잘 작성해야겠다 생각했습니다.정리 : https://silvercastle.notion.site/1-aea485f7cd5a433a8fb52715b16a81c9?pvs=4 Day2강의POST요청을 받기위해 Body에 데이터를 담아 보낸다.Body에 데이터를 보내는 방법으로는 JSON형태로 보내면 된다.JSON이란?데이터를 쉽게 교환하고 저장하기 위한 표준 방법형식은 (키 : 값) 형식으로 되어있으며 자바에 자료구조인 Map과 유사합니다.작성 방법1. 처음 작성시 중괄호가 들어간다 배열은 []로 생성하면된다. { //중괄호 생성 객체가 생성이 되었습니다. }2. 중괄호 안에 (키,값)형태로 표기 숫자는 큰따옴표가 들어가지 않으며, 속성은 쉼표로 구분합니다. 배열 작성시 위에 내용과 마찬가지로 []을 넣으면된다. JSON에 또 다른 객체를 넣고 싶다면 {}를 넣어 표현하면 됩니다.{ "name" : "이은성", "age": 1000, "pocket" : ["핸드폰","지갑"], "info" : { "address" : "서울", "phoneNum" : "010-1111-1111" } }서버(SpringBoot)에서는 어떤 방식으로 POST요청에 대한 Body 데이터 값을 받을까? { "number1": 1, "number2": 2 }public class CalculatorAddRequest { private final int number1; private final int number2; //생성자.. //getter }@PostMapping("/multiply") public int multiplyTwoNumbers(@RequestBody CalculatorAddRequest request){ return request.getNumber1() * request.getNumber2(); }@PostMapping : POST방식으로 요청을 받기 위한 어노테이션요청한 Body에 대한 key값과 DTO에 정의한 key값은 일치 해야합니다.@RequestBody : HTTP Body로 들어오는 JSON을 변환해주는 어노테이션CalculatorAddRequest에 요청받은 JSON값을 넣어준다.유저 생성/조회 API 개발사용자를 등록하려면 POST방식을 사용하며, 이름(필수)과 나이(선택)에 따라 요청이 들어왔습니다.요청을 받은 값을 Controller에 받아 List 자료구조에 값을 저장했습니다.유저를 조회하기 위해서 List.get(index)를 활용해야하는데 이것이 id값으로 id는 겹치지않은 유니크한 값입니다. 고유한 번호로 있으면 중복되는 문제는 없습니다.여기서 문제점은 List에 저장하는 방식은 메모리에 저장하는 방식으로 웹서버를 재시작하는 경우, 기존에 있던 값들이 날아가는 문제가 있어 서비스 운영이 어려워집니다. 이를 해결하기 위해선 어떤 방안이 있을까요?미션첫번째 미션은 두 수를 입력하면 다음과 같은 결과가 나오는 GET API를 만들자제가 설계한 방법으로는 요구사항대로 첫번째 쿼리스트링이기때문에 @RequestBody를 사용하지 않아야 된다 생각했으며, 강의에서 배운 DTO객체를 request와 response를 나눠 작업을 진행했습니다.첫번째 문제는 크게 어렵지 않았습니다.두번째 문제는 날짜를 입력하면 무슨 요일인지 알려주는 GET API를 만들어보는 것이였습니다.코치님이 조언해주기를 LocalDate를 활용해보라해서 넘어오는 날짜 : yyyy-mm-dd 형식으로 받아오는것을 String으로 받아와 자바에서 지원하는 LocalDate를 활용하여 String값을 split하여 of에 넣어주는 작업을 진행 후 getDayOfWeek과 getDisplayName에 있는 메서드를 활용하여 진행했습니다.하지만 추가적으로 LocalDate를 사용하는 방법중에 yyyy-mm-dd를 직접넣어 파싱해주는 메서드를 찾았고 그것을 활용하여 사용해봤습니다.코치님께서 모든 과제를 확인후 String을 받지않고 직접 LocalDate를 파라미터 값으로 받아 사용하면 훨씬 코드가 깔끔해진다는 내용을 듣고 앞으로 설계할때 고려해야겠다 생각했습니다.LocalDate나 LocalDateTime에 대해 자주 사용해보지 않았지만, 내용들을 이해하며 사용해보니 많이 와닿았습니다.[노션] : https://silvercastle.notion.site/2-3d17d8447562464eb081c340bbfbdfc8?pvs=4[깃허브] : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/learn/controller/example Day3강의Day2에서 고민한 내용에 대한 답변은 데이터베이스를 활용하여 데이터를 저장하는 방식을 사용하면 좋습니다.실무에서 회사내에서 데이터베이스를 어떤방식으로 활용하면 좋을지 고민을 많이 했고, 아직 많이 부족한 실력이라 인지하고 있습니다. 또한 RDBMS를 지금까지 MariaDB,PostgreSQL을 사용해보았고 물론 크게 다른 점은 없지만 MySQL을 활용해보는 계기가 되었습니다.DDL : 데이터 정의 언어로 데이터를 생성, 수정, 삭제 등의 데이터 전체 골격을 결정하는것을 이야기합니다.create database [데이터베이스명] : 데이터베이스(스키마)를 만드는 명령어입니다.show database : 현재 생성되어있는 데이터베이스를 확인하는 명령어입니다.drop database [데이터베이스명] : 데이터베이스(스키마)를 삭제하는 명령어show table : 테이블 목록을 보는 명령어create table [테이블명] ( 필드이름 속성 부가 기능 primary key(필드) ) : 테이블을 생성하는 명령어 입니다.속성에는 정수,실수,문자열,날짜등이 있습니다.id값은 표현을 할수있는 정수의 크기가 많은 long을 사용하는 것이 좋습니다 int는 2의32 long은 2의64이기때문입니다.DML : 데이터에 대한 CRUD(생성, 조회, 수정, 삭제)에 대한 데이터를 관리합니다.INSERT INTO [테이블 이름] (필드1, 필드2) VALUES (필드1값,필드2값) : 테이블에 값을 생성하는 명령어입니다.필드1,필드2 = 필드1값,필드2값 이런식으로 위치를 맞춰줘야하며 필드값이 달라지면 안됩니다. 순서가 매우 중요합니다 원하지 않은 데이터가 들어갑니다.Select [조회할 필드 명] from [테이블] where [조건 문] .. : 테이블 안에있는 원하는 필드명을 가져와 조건에 따라 조회하는 명령어입니다.조건문의 연산자로는 and,or,between,IN 등등이 있습니다.Spring에서 Database 연결하는 방법spring: datasource: username: "root" password: "1234" url: "jdbc:mysql://localhost:3306/library" driver-class-name: com.mysql.cj.jdbc.Driveryml방식으로 데이터베이스에 필요한 정보를 가지는 명령어입니다.아이디, 비밀번호, url, driver로 구성되어있으며, 만약 일치하지 않거나 DB서버가 죽었을경우 컴파일 과정에서 문제가 발생합니다.코드에서 직접 구성하기기존에 있는 코드에서 List를 지우고 JdbcTemplate을 사용하여 쿼리를 직접 작성하고 사용해보는 작업을 진행했습니다.String sql =""; 으로 쿼리를 제작하고jdbcTemplate메서드를 사용하여 jdbcTemplate.update, jdbcTemplate.query를 활용하여 CRUD의 기능을 구현했습니다. 여기서 update는 업데이트문이 아닌 쿼리 변경에 대한 업데이트처리를 위한 메서드입니다.저는 시작을 jdbcTemplate이 아닌 Mybatis와 JPA같은 기술스택을 먼저 사용해보고 이후로 사용을 했는데 예전 개발자님들은 상남자이며, 정말 예외에 대한 처리를 하나하나생각하기 머리가 아팠을꺼같습니다. 그리고 이전기술을 사용하니 JPA와 MyBatis와 같은 기술들이 얼마나 간편하고 좋은지에 대해 알아갔습니다.미션과제에 대한 내용중에 익명클래스,람다,Stream문 ,함수형 프로그래밍 @FunctionalInterface, 메소드 레퍼런스등을 정리하며 기존에 인텔리제이에서 제안 및 변환작업만 자동으로 진행했지 이게 무슨 내용인지에 대해 찾아보려 하지 않았습니다. 정말 인텔리제이는 좋은 도구지만 용도를 더 잘 알고 사용하는 것이 나중에 1000퍼 더 이해되기 쉽다를 느꼈습니다.또한 익명클래스와 람다식에 대한 차이점과 람다식을 왜 등장했는지에 대한 내용을 정리하며, 동작원리를 이해하고 이래서 익명 클래스에 대한 불편함과 그로인해 람다를 사용하는 방법을 알았습니다. 점점 더 내안의 "왜?"를 채울 수있어서 좋았습니다.[노션] : https://silvercastle.notion.site/3-f34ffaa6ae8147d58f24659af459bf9e?pvs=4 Day4강의기존에 배웠던 내용을 가지고 유저 업데이트API, 삭제 API 개발과 테스트를 진행을 해보았습니다. HTTP body{ "id" : Long, "name" : String }HTTP Response : Void // 200 OK! HTTP Method : PUTHTTP Path : /user@PutMapping("/user") public void updateUser(@RequestBody UserUpdateRequest request){ String sql = "update user set name = ? where id = ?"; jdbcTemplate.update(sql, request.getName(),request.getId()); } HTTP Method : DELETEHTTP Path : /user@DeleteMapping("/user") public void updateUser(@RequestBody UserUpdateRequest request){ String sql = "delete from user where name = ?"; jdbcTemplate.update(sql, request.getName()); } 이전 강의에서 마찬가지로 코드를 sql문과 update를 활용하여 데이터베이스 변경작업을 진행했습니다.기존에 준 웹 UI를 가지고 변경과수정 작업을 진행했습니다. 또한 예외처리를 배웠는데 사용자가 없을경우? 이런저런것에 대한 처리가 되어있지않아 서비스를 운영할때 큰 문제로 다가올 수 있습니다.String readSql = "SELECT * FROM user WHERE id= ?"; boolean isUserNotExist = jdbc.query(readSql,(rs,rowNum) 0,request.getId()).isEmpty if(isUserNotExist){ throw new IllegalArgumentException(); }사용자의 정보에 대해 조회를하고 만약 존재하지 않는다면 List가 공백으로 나오며 , 비어있다면 true 비어있지 않으면 false로 반환하는 코드를 작성 후 인자값 오류에 대한 예외처리를 진행했습니다. 이것말고 다른 예외처리도 생각을 꼭꼭해야합니다. 그래서 개발하기 전에 어떤 문제를 야기할지 생각한 후 코드를 개발하는것이 더 좋다 생각합니다. 하지만 Controller메서드들에 너무 많은 기능과 역할을 하고있다 생각하고 있습니다. 나중에 코드가 1000줄이상 늘어나면 보기 복잡해질 것이며, 가독성과 유지보수성이 떨어져 개발하면서 어려움을 겪을 수있습니다. 나중에 Day5의 강의의 클린코드를 통해 변경하는 작업을 진행해야겠다 생각했습니다. 미션문제별로 설계 방법은 첫번째로 어떤 table을 제작해야할지, table안에 어떤 column을 생성하면 좋을지 분석 및 구현을 진행했으며, 두번째로 패키지구조를 나눠 각 메서드마다 역할분담을 해야한다 생각했습니다.문제를 해결하면서 3번문제를 잘못 읽는바람에 시간이 소요되는 문제가 있었습니다. 항상 개발자는 요구사항에 대한 분석을 해야한다 느꼈습니다.시간이 부족한탓에 예외처리를 완벽하게 처리를 하지 못했는데 나중에 그것도 설계해서 예외처리를 둬야겠다 생각했습니다.[노션] : https://silvercastle.notion.site/4-9621a06cc0d24a63871a9f50e8c6cd48?pvs=4[깃허브] : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/daliy/dayfourDay5강의CleanCode의 중요성개발자는 요구사항을 구현하기 위해 코드를 읽고 작성그런데 코드가 만약 지저분하고 더럽다면 코드를 이해하는데 많은 시간이 들어 유지보수성이 떨어진다.좋은 코드는 코드만봐도 어떤 목적을 가진 것인지 확인할수 있다.더 나아가서 이전에 작성했던 코드들에 대해 생각을 해보면 Controller에 모든 기능을 구현하면 안될까?네 메서드는 하나의 기능을 수행하는 것이 좋습니다.@GetMapping("/user") public List getUsers() { String sql = "select * from user"; return jdbcTemplate.query(sql, new RowMapper() { @Override public UserResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException { long id = rs.getLong("id"); String name = rs.getString("name"); int age = rs.getInt("age"); return new UserResponseDto(id, name, age); } }); }해당 코드는 조회하는 쿼리에 대해 대한 컨트롤러 메서드입니다. 딱 봐도 기능적 분리가 필요해보입니다.sql문은 어떤식으로 분리하면 좋을까?라는 생각과 비지니스로직을 어디다 분리 할까 생각하다 Layered Architecture에대해 배운 내용을 바탕으로 나눴습니다.Controller : HTTP Body 요청을 받아 객체로 변환하는 계층Service : 비지니스로직(현재 유저 확인)기능이 동작하기 위한 작업을 진행하는 계층Repository : 데이터베이스와 접근 및 상호작용을 하기위해 나타나는 계층 UserController @GetMapping("/user") public List getUsers() { return userService.getUsers(); } UserService public List getUsers() { return userRepository.getUser(); } UserRepository public List getUser() { String sql = "select * from user"; return jdbcTemplate.query(sql, (rs, rowNum) -> { long id = rs.getLong("id"); String name = rs.getString("name"); int age = rs.getInt("age"); return new UserResponseDto(id, name, age); }); }해당 3계층으로 나눠 진행했습니다! 해당 기능을 분리를 진행했으며, 이전 코드에 비해 깔끔해졌으며 나중에 코드를 추가및 변경할때 해당 계층으로 들어가 작업을 하면 됩니다. 미션오랜만에 코딩테스트 문제 풀듯이 풀어 재밌었다.첫번째방식은 기존에 긴 코드에 대한 기능에 대한 분리를 진행을 해봤습니다. 객체지향 SOLID 특징 중 SRP(단일 책임 원칙)을 생각하여 나누려고 진행했습니다. 나눈 기능에 대한 세부 동작을 나눴으며, 그로 인해 클래스를 나눠 구현하도록 설계를 진행했습니다. 또한 애매한 변수 네이밍, 메서드 네이밍에 대한 수정도 이루어지도록 생각을 했습니다.코드를 수정후 한 걸음 더에 대한 요구사항에 대한 제 분석은현재 코드는 1~6이지만 동적으로 변경할 수 있는 방법주사위의 시행 횟수를 동적으로 변경하는 방법해당 방법 2가지를 생각했으며, 그에 따라 정적배열, for문등등을 활용하여 작업을 진행했습니다. [노션] : https://silvercastle.notion.site/5-89b621e69f094263af872580ac19324c?pvs=4[깃허브] : https://github.com/backgom1/Inflearn_BE0_Study/tree/main/ens-library-app/src/main/java/com/group/enslibraryapp/daliy/dayfour 1일차 전체 후기첫 주차정리를 했는데 문서에 대한 내용이 너무 두서없이 작성되어 다음 주차부터는 깔끔하게 문서 작성하도록 노력해야겠다 생각했습니다. 내가 알고있다고 생각했던 내용에 대해 다시 들으니 아 이런 내용을 몰랐다고? 하는 내용도 많고 기존에 알고있던 내용도 혼동되었습니다. 내가 알고있다고 자만?하지말고 계속 기본기를 쌓아 공부를 해야겠습니다.코치님에게 질문을 드렸고 만족한 대답을 들어서 너무 행복하고, 몰랐던 내용과 애매한 내용에 대해 배울 수 있어서 너무 좋았습니다. 회사를 다니면서 강의를 듣는 상황이 오니 좀 더 체계적으로 시간을 잡아 진도를 따라가야겠다 생각했습니다.