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 Part1 Weekly Mission 제출합니다. #897

Merged
merged 58 commits into from
Oct 24, 2023

Conversation

uijin-j
Copy link

@uijin-j uijin-j commented Oct 19, 2023

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

바우처 관리 Command-line Application

요구사항

  • create 커맨드 지원
    • 생성 가능한 바우처 리스트 (FixedAmountVoucher, PercentDiscountVoucher)
  • list 커맨드 지원
    • 생성된 바우처 리스트 조회
  • exit 커맨드 지원
    • 프로그램 종료
  • blacklist 커맨드 지원
    • 블랙 리스트 명단 조회
    • 명단은 CSV 파일로 부터 가져옴
  • 에러 로그는 파일로 기록
  • 바우처 정보를 파일로 기록

구현 내용

diagram-export-2023 -10 -19 -오후-11_50_40

VoucherManagementSystemApplication

  • VoucherApplication 실행

VoucherApplication

  • Console 클래스를 이용해 사용자 입력을 받고 적절한 Service를 호출

CommandType

  • 사용자가 이용할 수 있는 기능을 모아둔 Enum

Console

  • 콘솔 입출력 관련 유틸리티 클래스

VoucherService

  • 바우처 관련 비즈니스 로직 수행
  • 비즈니스 로직에 맞게 적절한 Repository 메서드 호출

BlacklistService

  • 블랙리스트 관련 비즈니스 로직 수행
  • BlacklistRepository에게 요청을 전달

VoucherRepository

  • 저장소(파일, 메모리 등)에서 바우처 데이터를 가져와 서비스에게 제공해 줄 기능들을 명시해 놓은 인터페이스

MemoryVoucherRepository

  • 메모리로 바우처 정보를 관리하는 VoucherRepository 구현 클래스

JSONFileVoucherRepository

  • voucher_data.json 파일에서 바우처를 관리하는 VoucherRepository 구현 클래스

LoadedJSONVoucher

  • JSON으로 저장된 바우처 정보를 매핑하는 클래스
  • VoucherType 필드에 맞는 바우처 인스턴스를 반환

Voucher

  • 바우처 정보를 가지고 있는 모델 인터페이스

VoucherType

  • 바우처 종류를 모아둔 Enum

FixedAmountVoucher

  • 고정 금액 할인 바우처를 위한 Voucher 구현 클래스

PercentDiscountVoucher

  • 비율(%) 할인 바우처를 위한 Voucher 구현 클래스

User

  • 사용자 정보를 가지고 있는 모델 클래스

✅ PR 포인트 & 궁금한 점

  • 프로젝트를 진행하면서 클래스와 메서스, 변수명을 정하는 것이 고민이었습니다.

    ex) LoadedJSONVoucher, VoucherManagementSystemApplication, VoucherApplication, selectCommand(메서드명)

    🙋🏻‍♂️ 현재 네이밍이 적절하게 되어있는지, 그리고 추천하는 네이밍 방식이 있을지 궁금합니다!

  • VoucherApplication 의 실행 방식으로
    (1) 재귀적인 메서드 호출 (현재)
    (2) while 문으로 루프를 도는 것
    을 고민했는데, 어떤 방식을 사용하는 것이 좋았을까요?

  • 현재 저희 어플리케이션에서는 바우처 종류에 상관없이 바우처 정보를 하나의 파일에 저장하고 있습니다. 그렇기 때문에 JSONFileVoucherRepository 에서는 바우처 데이터를 불러올 때 모든 종류의 바우처 정보를 가지고 있는 LoadedJSONVoucher 클래스로 매핑해서 가져오는데 이 부분을 어떻게 개선할 수 있을지 궁금합니다!

    ❗ (문제점) 바우처 종류가 늘어날수록 LoadedJSONVoucher 클래스가 갖고 있어야 할 필드가 늘어남

    🙋🏻‍♂️ 바우처 종류에 따라 파일을 분리해서 저장하는 것이 좋을까요?

    🙋🏻‍♂️ 비슷하게 만약 RDB로 관리한다면 모든 종류의 바우처를 하나의 테이블로 관리하는게 좋은 방식일까요?

  • 바우처 정보를 파일에 언제 저장하면 좋을지 궁금합니다!
    (1) 프로그램이 종료될 때 한번에 저장 (현재)
    (2) 바우처 Create 요청이 올 때마다 저장
    (3) 적절한 주기(ex. 1시간, 1일 간격)에 저장

  • 블랙리스트 관련 비즈니스 로직을 UserService에 넣는 것이 좋을지, 아니면 별도의 BlacklistService(현재)에 넣는 것이 좋을지 궁금합니다!

uijin-j and others added 30 commits October 13, 2023 16:30
################
# <타입> : <제목> 의 형식으로 제목을 아래 공백줄에 작성
# 제목은 50자 이내 / 변경사항이 "무엇"인지 명확히 작성 / 끝에 마침표 금지
# 예) feat : 로그인 기능 추가

# 바로 아래 공백은 지우지 마세요 (제목과 본문의 분리를 위함)

################
# 본문(구체적인 내용)을 아랫줄에 작성
# 여러 줄의 메시지를 작성할 땐 "-"로 구분 (한 줄은 72자 이내)

################
# 꼬릿말(footer)을 아랫줄에 작성 (현재 커밋과 관련된 이슈 번호 추가 등)
# 예) Close prgrms-be-devcourse#7

################
# feat : 새로운 기능 추가
# fix : 버그 수정
# docs : 문서 수정
# test : 테스트 코드 추가
# refact : 코드 리팩토링
# style : 코드 의미에 영향을 주지 않는 변경사항
# chore : 빌드 부분 혹은 패키지 매니저 수정사항
################
################
# <타입> : <제목> 의 형식으로 제목을 아래 공백줄에 작성
# 제목은 50자 이내 / 변경사항이 "무엇"인지 명확히 작성 / 끝에 마침표 금지
# 예) feat : 로그인 기능 추가

# 바로 아래 공백은 지우지 마세요 (제목과 본문의 분리를 위함)

################
# 본문(구체적인 내용)을 아랫줄에 작성
# 여러 줄의 메시지를 작성할 땐 "-"로 구분 (한 줄은 72자 이내)

################
# 꼬릿말(footer)을 아랫줄에 작성 (현재 커밋과 관련된 이슈 번호 추가 등)
# 예) Close prgrms-be-devcourse#7

################
# feat : 새로운 기능 추가
# fix : 버그 수정
# docs : 문서 수정
# test : 테스트 코드 추가
# refact : 코드 리팩토링
# style : 코드 의미에 영향을 주지 않는 변경사항
# chore : 빌드 부분 혹은 패키지 매니저 수정사항
################
Voucher 필드 게터 추가
Copy link

@ksy90101 ksy90101 left a comment

Choose a reason for hiding this comment

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

간단하게 질문 남겼어요 고생하셨어요 :)


import static java.text.MessageFormat.format;

public class FixedAmountVoucher implements Voucher {

Choose a reason for hiding this comment

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

inteface로 구현하는게 좋을까요?
현재 중복코드들이 있는것 같은데요.
이런 경우 상속 또는 조합 방식이 더 어울려 보여요.

Copy link
Author

Choose a reason for hiding this comment

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

Voucher를 추상클래스로 변경했습니다. 확실히 중복되어 있던 코드가 사라져서 더 깔끔해 진 것 같습니다! 조언 감사합니다:)

Comment on lines 17 to 19
if (amount <= 0) {
throw new IllegalArgumentException(format("{0}: 할인 금액은 0 보다 커야 합니다.", amount));
}

Choose a reason for hiding this comment

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

validate는 따로 메서드를 빼서 의미를 전달하면 어떨까요?
아울러 amount의 값을 int 이상의 값을 입력하면 어떻게 될까요?

import static java.text.MessageFormat.format;

public class PercentDiscountVoucher implements Voucher {
private static final Logger logger = LoggerFactory.getLogger(FixedAmountVoucher.class);

Choose a reason for hiding this comment

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

Suggested change
private static final Logger logger = LoggerFactory.getLogger(FixedAmountVoucher.class);
private static final Logger logger = LoggerFactory.getLogger(PercentDiscountVoucher.class);

Comment on lines 31 to 37
if (percent <= 0) {
throw new IllegalArgumentException(format("{0}: 할인율은 0% 이하일 수 없습니다.", percent));
}

if (percent > 100) {
throw new IllegalArgumentException(format("{0}: 할인율은 100%를 초과할 수 없습니다.", percent));
}

Choose a reason for hiding this comment

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

별건 아니지만, 하나로 합칠수 있지 않을까요?

Comment on lines 21 to 23
public String getInfo() {

return format("id: {0}, 고객명: {1} ", id, name);

Choose a reason for hiding this comment

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

Suggested change
public String getInfo() {
return format("id: {0}, 고객명: {1} ", id, name);
public String getInfo() {
return format("id: {0}, 고객명: {1} ", id, name);

try (BufferedReader reader = Files.newBufferedReader(Paths.get(path), StandardCharsets.UTF_8)) {
reader.readLine(); // skip header
reader.lines()
.map(s -> s.split("[;,]"))

Choose a reason for hiding this comment

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

상수화를 하면 어떨까요?

}

public List<User> findAll() {
return blacklist;

Choose a reason for hiding this comment

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

불변으로 return하면 어떨까요?
unmofiedList라는 키워드를 한번 찾아보세요.

Copy link
Author

Choose a reason for hiding this comment

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

Collections.unmodifiableList()를 사용해서 변경했습니다!


@Profile("dev")
@Repository
public class JSONFileVoucherRepository implements VoucherRepository, DisposableBean {

Choose a reason for hiding this comment

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

(질문)
DisposableBean 해당 빈을 왜 구현하신걸까요?

Copy link
Author

@uijin-j uijin-j Oct 24, 2023

Choose a reason for hiding this comment

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

저희 프로그램은 프로그램이 종료될 때, 캐시되어 있던 Voucher 정보를 파일에 저장합니다!
그래서 프로그램이 종료되면서 빈이 사라질 때, 자동으로 파일 저장기능을 수행하도록 하기 위해 DisposableBean을 구현했습니다.
파일 저장 기능을 일반 인스턴스 메서드로 빼서 서비스 쪽에서 프로그램을 종료할 때, 해당 로직을 호출하는 형식으로 변경하는 것이 나을까요?

사실 파일에 저장하는 것과 관련해서는 파일 저장을 프로그램 종료 시 가 아닌 프로그램 중간중간에 해야할 것 같다는 생각이 드는데, 어떤 방식이 좋을까요? 저희가 생각했던 방식은

  1. 프로그램이 종료될 때 한번에 저장 (현재)
  2. 바우처 Create 요청이 올 때마다 저장
  3. 적절한 주기(ex. 1시간, 1일 간격)에 저장

이 있습니다! 2번은 오버헤드가 있을 것같고, 3번이 가장 적절해 보이는데 추후 3번 방식으로 변경해도 괜찮을까요?

}

ObjectReader objectReader = objectMapper.readerForListOf(LoadedJSONVoucher.class);
List<LoadedJSONVoucher> loadedList;

Choose a reason for hiding this comment

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

좀더 의미잇는 단어를 쓰면 어떨까요?
loadedVouchers 같은 의미로요. List라는 단어는 포괄적으로 사용하는 단어니깐요.

Copy link
Author

Choose a reason for hiding this comment

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

jsonVouchers로 변수명 변경했습니다!

import java.util.Objects;

public final class Console {
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

Choose a reason for hiding this comment

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

혹시 Scanner대신 BufferedReader를 쓰신 이유가 따로 있나요

그냥 질문입니닷.

Copy link
Author

@uijin-j uijin-j Oct 24, 2023

Choose a reason for hiding this comment

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

저는 주로 Scanner을 쓰고, 현우님은 주로 BufferedReader를 써서 별 고민없이 BufferedReader을 썼던 것 같습니다.. 찾아보니 Scanner와 BufferedReader의 차이에는

  1. 버퍼 사이즈 (Scanner: 1KB, BufferedReader: 8KB)
  2. Input 형식 (Scanner: nextInt() 등의 메서드로 다양한 type 지원, BufferedReader: only String)
  3. Thread safe (Scanner: thread safe X, BufferedReader: thread safe O)

가 있는 것 같습니다.

🙋🏻‍♀️ 동기화 이슈로 BufferedReader를 쓰는게 적합해 보이는데, 그대로 BufferedReader를 써도 될까요?

메서드 역할을 구체적으로 명시
기존 상위 모듈인 CommandType 이넘과 
VoucherApplication을 application 패키지로 이동
VoucherApplication 이름을 ConsoleApplication으로 변경
기존 loadedList -> loadedVouchers
@ksy90101 ksy90101 merged commit 3c688df into prgrms-be-devcourse:uijin-j/week1 Oct 24, 2023
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.

3 participants