Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5기 - 이세희] SpringBoot Part3 Weekly Mission 제출합니다. #965

Open
wants to merge 397 commits into
base: Sehee-Lee-01/week3
Choose a base branch
from

Conversation

Sehee-Lee-01
Copy link
Member

@Sehee-Lee-01 Sehee-Lee-01 commented Nov 5, 2023

✅ 3 주차 코드 리뷰 반영 사항 (진행중)

  • view controller 테스트 코드 작성.
    • CustomerThymeleafControllerTest.java
    • VoucherThymeleafControllerTest.java
  • @RestController 적용하기.
  • ResponseEntity 적용하기. + 상태 코드 반영
  • 상수에 대한 생각을 다시 해보기.
    • SQL 쿼리는 JdbcRepository에 바로 보일 수 있도록 적용
  • @Transactional(readOnly=true)는 왜 쓰는지 알아보기.(아래에 간단하게 메모해놓겠습니다!)
    • 현재 만든 어플리케이션에서는 "조회"만 하는 경우라면 @Transactional(readOnly=true)를 붙여도 안붙여도 겉으로 보여지는 기능은 비슷하게 작동한다.
      • 대신 DB 시스템(ex. MySQL, MariaDB, PostgreDB... 등등)에 따라서 해당 어노테이션을 붙이고 CUD를 하게되면 예외를 발생시켜주는 경우도 있어서 이를 지원해주는 DB 시스템을 사용한다면 실수를 막기 용이하다.
      • 개발하는 개발자가 보기에는 애너테이션을 통해 명시적으로 조회를 위한 기능이라는 것을 파악할 수 있다는 장점이 있다.
      • 또 OSIV 설정이 꺼진 시점에서 붙인 것과 안 붙인 것에서 차이가 난다고 하는데, JPA, 영속성 컨텍스트 등의 개념을 알아야 더 잘 이해가 될 것 같아 이 부분은 더 찾아보겠습니다!
    • DataSource가 여러 개일 때, 예를 들어 읽기 전용 DB, 쓰기 전용 DB가 있고, 이를 Spring에 등록해주면(RoutingDS 같은 것이 있다고 한다.) 해당 @transactional(readOnly=true)가 붙은 메서드인 경우에는 쓰기 전용이 아닌 읽기 전용 DS로 교체해 데이터를 읽어온다고 한다. 이럴 때 더 장점이 부각되는 것 같다.
  • ControllerAdvice 알아보기.(@ExceptionHandler)

추가 수정 사항

📌 과제 설명

심화 기능은 구현하지 못하고 기본 기능만 구현했습니다!

3주차 과제 범위

Profile 종류

  • 직접 실행해보실 때 thyme, api, jdbc 설정해주시면 3 주차 과제 구현 기능 확인이 가능합니다!
  • 실행(중복 선택 가능)
    • thyme: thymeleaf 반환하는 컨트롤러 작동
    • console: console 컨트롤러 작동
    • api: api 컨트롤러 작동
  • DB: Voucher, Customer 레포지토리 작동(중복 선택 불가능)
    • memory : VoucherInMemoryRepository작동
    • file : VoucherFileRepository 작동
    • jdbc : VoucherJDBCRepository작동

👩‍💻 요구 사항과 구현 내용

(기본) 바우처 서비스 관리페이지 개발하기

  • Spring MVC를 적용해서 thymeleaf 템플릿을 설정해보세요.
  • 커맨드로 지원했던 기능을 thymeleaf를 이용 해서 관리페이지를 만들고 다음 기능을 지원가능하게 해보세요
  • 바우처
    • 바우처 조회 페이지: GET /vouchers
    • 바우처 상세 페이지: GET /vouchers/{voucherId}
    • 바우처 입력 페이지: GET /vouchers/new
      • 바우처 입력 요청: POST /vouchers/new
        # form Example
        { typeName: FIXED, discountValue: 10000 }
        
    • 바우처 삭제 기능: DELETE /vouchers/{voucherId}
  • 고객
    • 고객 조회 페이지: GET /customers
    • 고객 블랙리스트 조회 페이지: GET /customers/blacklist
    • 바우처 입력 페이지: GET /customers/new
      • 바우처 입력 요청: POST /customers/new
        # form Example
        { name: 이세희, isBlack: true}
        

(기본) 바우처 서비스의 API 개발하기

  • Spring MVC를 적용해보세요.

  • 바우처

    • 전체 조회기능: GET /api/v1/vouchers
    • 조건별 조회 기능
      • 바우처 생성 기간: GET /api/v1/vouchers/created-at?from={YYYY-mm-dd}&to={YYYY-mm-dd}
      • 특정 할인 타입: GET /api/v1/vouchers/type/{type}
    • 바우처 추가 기능: POST /api/v1/vouchers
      # Request Body Example
      { typeName: FIXED, discountValue: 10000 }
      
    • 바우처 삭제 기능: DELETE /api/v1/vouchers/{voucherId}
    • 바우처 아이디로 조회 기능: GET /api/v1/vouchers/{voucherId}
  • 고객

    • 전체 조회기능: GET /api/v1/customers
    • 블랙리스트 조회 기능: GET /api/v1/customers/blacklist
    • 고객 추가 기능: POST /api/v1/customers
      # Request Body Example
          { name: 이세희, isBlack: true}
      
  • JSON과 XML을 지원하는 REST API를 개발해보세요.

    • JSON 지원
    • XML 지원
  • (보너스) 바우처 지갑용 관리 페이지를 만들어보세요.

    • src/main/java/com/programmers/vouchermanagement/wallet에 잠시 구현한 것은 있지만 안보셔도 됩니다!

✅ 피드백 반영사항

  • Datasource 설정에서 user, password yaml로 관리하도록 반영 + 환경변수 설정
  • Voucher와 관한 값에만 의존하고 있는 Validator 삭제 후, VoucherType, Voucher 구조 리팩토링
  • 테스트 코드 피드백 반영
    • @jdbcTest 적용
    • rest 컨트롤러 테스트 작성

✅ PR 포인트 & 궁금한 점

  • 코드 리뷰 때 질문드리겠습니다!
  • 테스트 코드 부분은 시간 상 많이 반영을 못했습니다! ㅠㅠ 최대한 개인 리뷰 전까지 반영하도록 노력하겠습니다! 그래도 시간이 부족하면 개인 프로젝트 하면서 개인 프로젝트에 적용해보겠습니다!

✅ 실행환경 및 방법

실행 환경(JDK)

  • JDK17

Docker MySQL DB Server(애플리케이션, 테스트 코드 모두 사용) 연결 방법

  • docker image: haileylee/sehee_mysql:latest(docker hub에 올렸습니다!)
docker pull haileylee/sehee_mysql

docker run --name sehee-mysql -e MYSQL_ROOT_PASSWORD=20231028 -d -p 3306:3306 haileylee/sehee_mysql:latest

인텔리제이에 아래 정보로 DB 연결

  • src/main/resources/application.yaml 에 DataSource 설정
  • 연결 후 src/main/resources/schema.sql 에서 sql 쿼리 전체 실행

🚨⭐ 혹시 위 도커 이미지로 DB 서버가 실행이 잘 안된다면, 보통의 mySQL 서버에서도 실행 가능합니다!

아래 두 Config 클래스에서 DataSource 설정만 바꿔주신 후, src/main/resources/schema.sql 에서 sql 쿼리 전체 실행해주시면 감사하겠습니다!

애플리케이션 실행

image

  • 위 실행 설정에서 3개의 Profile(thyme, api, jdbc) 설정
  • 환경 변수 USER=root;PASSWORD=20231028 설정(입력란이 보이지 않는다면 위 사진에서 Modify Options를 눌러주고 환경변수 선택해주시면 됩니다!)
  • src/main/java/com/programmers/vouchermanagement/VoucherManagementApplication.java 에서 main 메서드 실행
  • ⭐ 실행 Tip: 페이지로 고객, 바우처를 생성한 후 api 확인해주시면 더 쉽게 확인하실 수 있습니다!

어플리케이션 테스트 진행

image

  • 환경 변수 USER=root;PASSWORD=20231028 설정(입력란이 보이지 않는다면 위 사진에서 Modify Options를 눌러주고 환경변수 선택해주시면 됩니다!)

return false;
}

if (menu.isIncorrect()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valid한 메뉴인지를 판단하는 function에서 Exit를 고르는것이 valid한 선택이고 Incorrect를 고르는게 invalid한 선택이 아닐까요?? incorrect일 때 false를 반환하는 것이 이해하기 어색하지 않을 것 같습니다!

}

public Customer(String name, boolean black) {
if (name == null || name.isBlank() || name.length() > 20)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation을 메서드로 분리하면 좋을 것 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 반영하겠습니다! 감사합니다ㅠㅠㅠ

public class Constant {
public static final String LINE_SEPARATOR = System.lineSeparator();
public static final String COMMA_SEPARATOR = ", ";
public static final int UPDATE_ONE_FLAG = 1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1이 의미하는 바가 잘 담겨있지 않은 네이밍 인 것 같습니다. 1이 update에서 성공했다는 의미라면 그런 부분을 잘 담은 네이밍을 하는 것이 가독 측면에서 더 좋을 것 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 좀 더 의미를 담도록 반영하겠습니다!

@@ -0,0 +1,20 @@
package com.programmers.vouchermanagement.util;

public class Message {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exception에 관란 메시지만 모아놓은 클래스라면 좀 더 자세한 클래스명을 쓰면 더 이해하기 좋을 것 같습니다.

return voucherService.update(voucherId, createVoucherRequest);
}

@GetMapping("/created-at")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query string을 이용한다면 /created-at path 없이 readAll에서도 구현할 수 있지 않을까 생각합니다.

return voucherService.readAllByCreatedAt(fromDateTime, toDateTime);
}

@GetMapping("type/{type}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 쿼리 스트링을 쓰는건 어떤가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#965 (comment)
위의 피드백과 함께 반영하겠습니다!

return "redirect:/vouchers";
}

@PutMapping("/update/{voucherId}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update와 같은 동사가 path에 들어가지 않는 것이 더 좋을 것 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다ㅠㅠㅠ 네이밍을 어떻게 할까 고민하다가 마감이 얼마 안남아서 급하게 작성했습니다...
이 부분도 반영하겠습니다!

@Transactional(readOnly = true)
public List<VoucherResponse> readAllVoucherByCustomerId(UUID customerId) {
List<Voucher> vouchers = walletRepository.findAllVoucherByCustomerId(customerId);
if (vouchers.isEmpty()) return Collections.emptyList();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

들여쓰기가 있으면 더 좋을 것 같습니다.

@Transactional(readOnly = true)
public CustomerResponse readCustomerByVoucherId(UUID voucherId) {
Optional<Customer> customerOptional = walletRepository.findCustomerByVoucherId(voucherId);
Customer customer = customerOptional.orElseThrow(() -> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 로직은 one line으로 써도 디미터 법칙을 위반하지 않을 것 같습니다!! 디미터 법칙은 내부 구조가 외부로 노출되지 않는다면 위반하지 않습니다!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants