본문 바로가기
해피 코딩/Today I Learned

[TIL 11] Chater 1. 과제와 피드백 내용

by happy-coding 2024. 8. 15.

 Today I Learned

Chater 1. 과제 제출에 대한 튜터님 피드백을 받게 되었다
튜터님께서 해주신 피드백은 Layered Architecture Pattern에 대한 내용이었다
🔥 경력이 많은 선임 개발자가 내 코드를 보고 해주는 조언인 만큼 새겨듣고, 문제를 파악하고, 기록하며 더 좋은 코딩 습관을 만들도록 하자!!

[ Chapter 1. 과제 발제 ]

  • MSA, 인메모리 저장소(Redis cache), 프로젝트 관리 심화과정을 응용해 보는 개인 프로젝트 과제입니다

그림 예시

패키지명 규칙과 포트 규칙 준수
  • 패키지명은 com.sparta.msa_exam 으로 설정하고 유레카 서버는 19090 포트로 실행되도록 설정해주세요.
  • 게이트웨이 서비스는 com.sparta.msa_exam.gateway 패키지로 추가하고 19091 포트로 실행되도록 설정해주세요.
  • 상품 서비스를 com.sparta.msa_exam.product 패키지로 추가하고 19093 포트로 실행되도록 설정해주세요.
  • 주문 서비스를 com.sparta.msa_exam.order 패키지로 추가하고 19092 포트로 실행하도록 설정해주세요.
  • 인증 서비스를 com.sparta.msa_exam.auth  패키지로 추가하고 19095 포트로 실행하도록 설정해주세요.
모든 API 의 Response Header 에Server-PortKey 로 현재 실행중인 서버의 포트를 추가해주세요.

사진 예시

개발 환경의 세팅을 아래와 같이 설정 해주세요.
  • Datasource
    • H2 memory database를 이용하시는 경우 무관합니다.
    • MySQL 로 생성하신 경우, dev 프로필의 설정하기 처럼 통일해주세요 
    • 설정하기: 포트: 3306 - 계정: root - 비밀번호: password - Database 이름: msa_exam
  • Redis 설정
    • 포트: 6379
    • 계정: default
    • 비밀번호: systempass
  • Zipkin 설정
    • 포트: 9411
  • JWT 만기 처리
    • JWT 토큰이 최소 1시간 이상 지속되도록 설정해 주세요. 과제 확인하는 도중에 토큰 만료로 인해 재발급받는 불편을 줄일 수 있습니다.

기본 API 구성하기
  • POST /products 상품 추가 API 
    • 상품 Entity
    • 상품 추가시 Request/Response 객체 구성은 자유입니다.
Key Value
product_id Long (Primary, Auto Increment)
name String
supply_price Integer

 

  • Get /products 상품 목록 조회 API
    • 응답 형태: List<응답 객체>
    • 응답 객체
    • 상품 목록 조회시 Request 객체 구성은 자유입니다.
Key Value
product_id Long
name String
supply_price Integer

 

  • Post /order 주문 추가 API
    • 주문 Entity
    • Request/Response 객체 구성은 자유입니다.
Key Value
order_id Long (Primary, Auto Increment)
name String
product_ids List<주문 매핑 상품 Entity>
  • 주문 매핑 상품 Entity
Key Value
id Long (Primary, Auto Increment)
order 주문 Entity
product_id Long

 

  • PUT /order/{orderId} 주문에 상품을 추가하는 API
    • 요청 Body
    • Request/Response 객체 구성은 자유입니다.
Key Value
product_id Long

 

  • GET /order/{orderId} 주문 단건 조회 API
    • 응답 객체
Key Value
order_id Long
product_ids List<Long>

 

  • GET /auth/signIn?user_id={string} 로그인 API
    • DB 연결이 되지 않아도 됩니다. Gateway 서비스의 Filter 만 통과할 수 있도록 구성해주세요.

상품 서비스는 라운드로빈 형식으로 로드밸런싱 구성하기
  1. IntelliJ Configuration 을 이용하여 상품 서비스를 19094 포트로 하나 더 실행 해보세요.
  2. 라운드로빈 형식으로 로드밸런싱을 구현해서 상품 서비스가 호출될 때마다 두 서비스를 반복하며 호출되게 구성해 보세요.
  3. 상품 목록을 조회할 때마다 API 의 응답 헤더의 Server-Port 값이 19093 , 19094 로 변경되어야 합니다.
주문에 상품을 추가하는 API 만들 때 아래와 같이 구성해보세요
  1. FeignClient 를 이용해서 주문 서비스에 상품 서비스 클라이언트 연결
  2. 상품 목록 조회 API를 호출해서 파라미터로 받은 product_id 가 상품 목록에 존재하는지 검증
  3. 존재할경우 주문에 상품을 추가하고, 존재하지 않는다면 주문에 저장하지 않음.
분산추적 구현해보기
  • 주문 서비스상품 서비스 에 Zipkin 을 연동하고, 호출시 Zipkin 대시보드에 Duration 이 측정 되는지 확인해보세요.
캐싱 기능 구현하기
  • 주문 조회 API 의 결과를 캐싱 처리하여 60초 동안은 DB 에서 불러오는 데이터가 아닌 메모리에 캐싱된 데이터 가 보여지도록 설정해주세요.
외부 요청 보호
  • Oauth2,JWT 기반으로 인증/인가를 구성하여 인가 없이 상품 서비스주문 서비스를 호출할 때 
    401 HTTP Status Code를 응답하도록 설정해주세요.
캐시를 더 활용 해볼까요?
  1. 상품 추가 API  를 호출 할 경우 상품 목록 조회 API 의 응답 데이터 캐시가 갱신되도록 구현해주세요.
  2. 상품 추가 후 상품 목록 조회 API 호출 시 데이터가 변경 되는지 확인합니다.

피드백 내용
프로젝트에 Layered Architecture 를 적용 해보세요!
레이어드 아키텍처(Layered Architecture) 란, 하나의 소프트웨어를 계층으로 분리하여 역할을 분리시키는 아키텍처 패턴입니다.
JPA에서는 크게 비즈니스와 가장 크게 밀접한 연관을 갖는 Entity, Repository 를 도메인(Domain) 영역이라 부르고, 핵심 비즈니스 로직이 구현되는 Service 를 Application 계층이라 부르며, 사용자의 입력 값을 받고 응답을 리턴하는 Controller 를 유저 인터페이스(UI, UserInterface) 계층이라고 부릅니다.
이러한 분리를 하는 이유는, "단방향 의존성" 을 위함인데 이것은 UI -> Service -> Domain 계층으로는 의존이 가능하나, Domain -> Service -> UI 계층으로는 의존이 불가하게 설계하는 것입니다.예를들어, Service(Applicaiton) 단의 DTO 의 name 이 name2 로 변경되었을때, 만약 Entity(Domain) 에서 해당 Dto 를 파라미터로 받고 있고, name 값을 사용하고 있으면 어떻게 될까요 ? Entity 에서 변경되어야 하는 값은 전혀 없으나 DTO 로 인해 Entity 의 로직에 수정이 발생하는 경우가 생깁니다.
위와 같은 경우가 잦게 발생하면 예상치 못한 사이드이펙트가 발생할 수 있으며, 오류의 부분을 찾기 위해 전체 소스를 찾아보는 등 디버깅에 시간적인 리소스를 많이 소요하게 됩니다. 그래서 Entity 는 Entity 만의 변경에 대응하고 Service 는 Service 만의 변경에 대응하는 형태로 계층간의 분리를 이용한 아키텍처 방식이 레이어드 아키텍처 방식입니다.
병준님이 진행해주신 프로젝트에는 Entity Product.java 가 Application 레이어인 ProductRequestDto 를 참조하고 있어 이 부분부터 수정 진행하면 될 것 같습니다 ㅎㅎ

문제
  • 도메인 계층인 Product(Entity)에서 서버 계층인 RequestDto를 사용
    • UI -> Application -> Domain 계층 방향으로 흐르는 단방향 의존성 설계 실패
@Getter
@Entity
@NoArgsConstructor
public class Product {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Long product_id;

  private String name;
  private Integer supply_price;

  public Product(ProductRequestDto requestDto) {
    this.name = requestDto.getName();
    this.supply_price = requestDto.getSupply_price();
  }

}

사실 피드백 내용이 더 있으나, 내용 중 한 부분만 가져왔다. 튜터님의 친절한 피드백에 사실 조금 감동받았다😭

튜터님께서 해주신 피드백 내용을 바탕으로 다음번에는 Layered Architecture Pattern에 대하여 공부하고, 공부한 내용을 바탕으로 프로젝트에 적용해 보도록 하자!!

 

읽어주셔서 감사합니다 😊

 

  • Layered Architecture pattern 을 사용하여 프로젝트 계층 분리하기

😺 Blog: https://happy-coding.tistory.com/24