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

[레거시 코드 리팩터링 - 3단계] 수달(허수진) 미션 제출합니다. ✨ #401

Merged
merged 30 commits into from
Nov 5, 2022

Conversation

her0807
Copy link

@her0807 her0807 commented Nov 3, 2022

객체 설계 너무 어려워 ..

좋은 개발자란, 의존성 관리를 잘해서 *유지보수하기 좋은 코드를 작성해야하고, 솔리드 원칙을 지켜야하고.. 테스트 코드도 꼼꼼하게 작성해야하고… 해야하는건 알지만 잘 모르는 영역(소위 말하는 DDD, 이벤트.. 등등 )이라 무서워서 학습을 미루고 있었어요.
그러던 중 step3 에 있는 우아한 객체지향 영상을 보고 조금이나마 방향을 깨달아서 실행해보았어요.

제가 원하는 리뷰는요 ..

  1. 처음으로 혼자서 JPA 를 사용해서 프로젝트를 진행해보았어요. 그래서 그로인해 발생하는 문제점이나 JPA 의 장점을 못살린 코드가 있는지 궁금해요.

  2. 의존성을 현재 1개 밖에 못찾았는데, 제가 방법을 잘 몰라서 그럴수 있겠다는 생각이 들었어요. 더즈의 의존성 찾고 끊어내는 방식을 알고 싶어요.

리뷰 후 방향

  • 현재 Mneu 히스토리에 대한 로직이 없어요. (리뷰 받고 나서 이벤트를 사용해서 해볼 생각이에요.)
  • 나 주문 받았어! 라고 order 가 이벤트를 발행하면, menu 히스토리를 관리하는 무언가를 해볼 수 있지 않을까 생각하고 있어요.

실행 플로우는 아래와 같이 진행했습니다. ☺️

1단계 스텝 (패키지를 분리해보자)

MVC 구조로 모든 도메인이 계층별로 나눠져 있는 상황입니다.
스크린샷 2022-11-04 오후 3 04 41

가장 쉽게 의존 관계를 파악하는 방법은 import 문을 확인하는 것입니다. 필드로 객체를 가지고 있든, 시그니처에 포함되어 있든 관계를 맺게 되면 방명록처럼 import 문으로 흔적이 남으니까요

이건 OrderService 에 import 있는 내용 .. 딱봐도 엄청 많다 😅
스크린샷 2022-11-04 오후 3 04 56

현재 연관, 의존 관계도

연관관계는 직선, 의존관계는 실선으로 표시

스크린샷 2022-11-04 오후 3 05 20

분석 해보기 !

일단 서비스 계층부터 살펴보았습니다.

큰 단위로 menu, order, table , product 이렇게 네가지로 분류할 수 있을 것 같네요!

하나의 서비스 객체에 연관관계가 3개씩 있고, 의존 관계 도메인이 2~ 3개 씩 존재하고 있어요

이건 분명 변경에 취약한 상황입니다.

지금 어떻게 해결할 수 있을까 ?

현재 할 수 있는 액션 플랜으로 패키지로 분리해서 해당 연관 관계를 끊어내보는 것이 생각났어요.

변경이 일어나도 같은 패키지 끼리만 일어나게!

스크린샷 2022-11-04 오후 3 05 36

Product 구조 바꾸기

prodcut 관련 api 를 실행 시키기 위해 필요한 클래스들을 아래와 같은 구조로 변경했습니다.

- product (최상단 패키지이름)
	- application (어플리케이션 서비스 로직이 들어가는 부분)
		- response (서비스에서 response 를 반환하기 때문에 둘의 의존관계가 긴밀하다고 판단)
	- domain (서비스의 근간이 되는 부분 - model - repository 도 model 에 엮여 있기 때문)
	- ui (presentation 영역, 요청이 들어올 때 사용됨)
		- request (사용자가 요청 처리 할 때 스펙에 따라 변경되기 떄문에 ui 와 가까움)

실제 변경된 Product 패키지 구조

스크린샷 2022-11-04 오후 3 06 08

그동안 얼마나 많은 의존 관계가 있었는지볼까 ?

위에 클래스들을 움직이니, 이 클래스들과 의존관계로 엮여 있던 클래스들에서 변경이 일어났습니다.

변경 시킨 클래스는 6개인데 files Changes 는 14개? 차차 정리해보자!
스크린샷 2022-11-04 오후 3 07 13

패키지를 분리한 뒤 계층 별 객체 관계확인

스크린샷 2022-11-04 오후 3 07 27

패키지끼리 단방향으로 흐르는지 확인할 것

보니까 양방향 참조를 하고 있는 것을 발견!
스크린샷 2022-11-04 오후 3 07 48

인터페이스로 의존관계 분리 !

검증 로직 자체가 order 랑 table 패키지 의존관계를 인터페이스와 , 구현체로 나눠서 각각 클래스가 있는 패키지로 분리하면 순환 참조 사이클을 풀 수 있었어요.
스크린샷 2022-11-04 오후 3 08 02

결과

TableService - TableGroupService 에서 더이상 OrderJpaRepository 를 참조하고 있지 않아 단방향 연관관계가 되었습니다! 단방향 연관관계를 가져야하는 이유는 영향이 생기는 방향을 단방향으로 흐르게 할 수 있기 때문이라고 생각합니다. A 가 변하면, B가 변하고, B가 변하면 A 가 변한다면 변경에 취약한 구조가 된다는 것을 리팩터링을 하며 file change 가 무분별하게 많이 일어날 때 느꼈어요.

@her0807 her0807 changed the title ☺️ [레거시 코드 리팩터링 - 3단계] 수달(허수진) 미션 제출합니다. ✨ Nov 3, 2022
Copy link

@ldk980130 ldk980130 left a comment

Choose a reason for hiding this comment

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

안녕하세요 수달~ 더즈입니다. 미션을 하면서 수달의 고민이 엄청 느껴져서 다소 가벼운 마음으로 미션을 하던 제가 다 부끄러워지네요ㅠ 최선을 다해 리뷰하면서 저도 많이 배워가겠습니다.

의존성에 관해

더즈의 의존성 찾고 끊어내는 방식을 알고 싶어요.

저도 의존성을 이렇게 찾고 끊는다!의 기준이 없는 것 같아요. 그래도 일단 양방향이나 순환 참조가 있는지는 확인하는 것 같아요. 거기서 끊는 방법은 엄청 다양할 것 같은데요. 수달이 사용한 인터페이스를 이용한 의존성 역전 방법도 있을 수 있겠죠.

현재 스모디 프로젝트 같은 경우에도 패키지 양방향 의존성이 존재해요. 그 원인이 복잡한 조회 로직이 포함되면서에요. 그래서 이를 개선해보고자 복잡한 조회를 하는 로직들을 제 3의 패키지로 분리하는 작업을 해볼까 생각하고 있어요. 핵심 비즈니스 로직과 복잡한 화면에 맞춘 api들은 변경의 라이프 사이클이 크게 차이가 나기도 하고요. 상황에 따라 의존을 끊는 방법은 다양할 것 같습니다!

메뉴와 주문의 관계에 관한 요구사항이 아직 만족되지 않은 것도 그렇고 몇몇 리뷰들도 반영해주시면 좋을 것 같아서 rc드립니다. 나머지 요구사항과 리팩터링이 어떻게 진행될지 기대가 됩니다ㅎㅎ

Comment on lines 62 to 72
private void validMenuProduct(final List<MenuProductRequest> menuProductRequests) {
for (MenuProductRequest menuProduct : menuProductRequests) {
validProduct(menuProduct.getProductId());
}
}

private void validProduct(final Long id) {
if (!productRepository.existsById(id)) {
throw new CustomIllegalArgumentException(NOT_FOUND_PRODUCT_EXCEPTION);
}
}

Choose a reason for hiding this comment

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

메뉴 상품 개수만큼 쿼리가 나갈 것 같은데요. 한 방에 확인할 수는 없을까요?

Copy link
Author

Choose a reason for hiding this comment

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

boolean existsProductsById(List productIds); 네임드쿼리 메서드로 해결 할 수 있을 것 같은데, 개선 된 로직에서는 이코드가 불필요해져서 제거했습니다 ㅎㅎ 감사합니다.

Comment on lines 74 to 75
//: todo 현재 등록된 메뉴 그룹이 있는가? 이게 여기 있는게 맞는가 ?
private void validMenuGroup(final Long menuGroupId) {

Choose a reason for hiding this comment

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

메뉴 그룹이 있는지에 대한 검증인 것 같은데 왜 여기 있는 것이 의심스러우신가요?

Copy link
Author

@her0807 her0807 Nov 4, 2022

Choose a reason for hiding this comment

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

처음 의존성을 찾을 때 단순한 생각으로 menuService 가 너무 많은 연관 관계를 맺고 있다는 느낌이 들었어요. 그런데 논리가 정확하지 않아서 코멘트로 만 남겨두었는데 알려주셔서 감사합니다.

++ 이후 그래프를 그려보니, 메뉴 그룹이 있는지 검증이 menuService 에 있는건 너무 자연스러운 일이더라고요. 그리고 의존 관계도 단방향으로 잘 흐르고 있고요. 그래서 여기 두어도 괜찮겠다고 판단했습니다 !

스크린샷 2022-11-04 오전 11 05 54

Comment on lines 28 to 29
@Enumerated
private Price price;

Choose a reason for hiding this comment

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

image

Copy link
Author

Choose a reason for hiding this comment

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

@Enumerated ㅋㅋㅋ .. @Embedded 로 변경하겠습니다..

private Long seq;
//:todo 객체로 바꾸기

Choose a reason for hiding this comment

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

객체로 바꾸면 클래스간 양방향 참조가 일어날 것 같은데 왜 객체로 바꾸고 싶으신가요?

Copy link
Author

@her0807 her0807 Nov 4, 2022

Choose a reason for hiding this comment

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

menuProduct 를 조회할 때 menu 를 가지면 엔티티 그래프 탐색이 용이해지기 때문에 좋다고 생각했는데 생각해보니 MenuProduct 를 단독으로 조회할 일이 없어서 필요없겠네요!

todo 로 마킹해두었던 부분들은 고민해보면 좋을 지점들을 표시해둔 것이었는데, PR 제출하면서 알리지 않았네요 😭

private Long seq;
//:todo 객체로 바꾸기
@Column(name = "menu_id", nullable = false, updatable = false, insertable = false)

Choose a reason for hiding this comment

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

updatableinsertable은 여기서 어떤 일을 해주나요?

Copy link
Author

@her0807 her0807 Nov 4, 2022

Choose a reason for hiding this comment

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

해당 칼럼에 대해서 update 나 insert 를 하게 된다면 변경 사항이 반영되지 않도록 막아두는 기능을 수행합니다.

insertable - 읽기 전용일 떄 사용함.
updatable - 읽기 전용이라서 수정을 막아둘 때 사용함

최초에 엔티티를 설계할 때 기존에 있던 필드를 그대로 가져갔는데, 에러가 생기는 상황이었어요.
그래서 읽기 전용으로만 두며 임시로 해결했는데, 의구심이 들어서 확인해보니까 재밌는 트러블 슈팅이 있었어요,
최초에 로직 작성할 때 menu 에서 이미 외래키의 주인을 menuProduct 로 두었기 때문에 필드로 두게되면 중복 필드가 발생하여 읽기 전용으로 바꾸니 해결이 되었던 거네요. 결국에는 중복을 제거하니 해결되었어요. 👍🏻
이 과정에서 객체 세계와 DB 세계에 차이를 다시한번 느끼고, 이 과정을 조금 더 편안하게 해주는 ORM 이지만 항상 의식적으로 의문을 가지며 사용해야한다는 깨달음을 얻었습니다.

해당 경험을 할 수 있게 해준 더즈의 피드백 감사드립니다.

Comment on lines 78 to 81
public void validExistOrderStatus() {
if (orderStatus.equals(COOKING) || orderStatus.equals(OrderStatus.MEAL)) {
throw new CustomIllegalArgumentException(INVALID_TABLE_UNGROUP_EXCEPTION);
}

Choose a reason for hiding this comment

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

테이블 그룹을 해제할 때 사용하는 메서드네요. 도메인에게 로직을 맡기는 것은 좋지만 과연 Order의 책임인가 생각해보게 되네요.

자신이 완료되지 않았다면 그룹을 해제할 수 없다는 메시지의 예외를 터뜨리는데 TableGroup과 소스 코드에서의 의존은 없지만 논리적인 의존이 생기는 것 같아요.

Copy link
Author

@her0807 her0807 Nov 4, 2022

Choose a reason for hiding this comment

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

오오 논리적인 의존 !!!! 이거 엄청 멋진 키워드인데요 ? ☺️
눈에 보이는 의존만 찾아서 정리하고 있었는데, 논리적인 의존도 의존이네요.
조금더 넓은 시야를 주셔서 감사합니다. 그룹을 해지할 때 주문 상태에 대해서 무조건 알아야하는데, 그 값이 Order 에 있어서 Order 에 메세지를 보내는 방향으로 협력 관계를 구축했었는데,

더즈의 말씀을 듣고 보니 결국에 상태를 알려면 Order 까지 접근이 필요하니까 논리적인 의존이 맞네요. ㅠㅠ
따라서 DB 에 데이터가 있으니까 DB 에게 물어보는 방향으로 바꿔서 객체간 의존도를 낮춰보도록 하겠습니다

Comment on lines 77 to 81
private void clearOrderTables(final TableGroup tableGroup) {
for (final OrderTable orderTable : tableGroup.getOrderTables()) {
orderTable.clear();
}
}

Choose a reason for hiding this comment

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

  1. TableGroup 안에 List<OrderTable>이 있으니 getter를 통한 로직이 아닌 메시지를 던져보는 건 어떨까요? (tableGroup.ungroup())

2.. TableGroupOrderTable 사이의 연관을 끊는데 OrderTable만 끊어주네요. 물론 연관관계의 주인이 OrderTable이기 때문에 DB상에서 관계는 잘 끊어질 것 같아요. 하지만 이 트랜잭션 안에서는 객체 참조로 TableGroup이 여전히 List<OrderTable>을 가지고 있어요. 메서드 수행 후 TableGroup안의 리스트가 비었는지 검증하는 테스트가 있다면 실패하겠죠. 객체 간의 관계도 함께 끊어주는건 어떤가요?

Copy link
Author

@her0807 her0807 Nov 4, 2022

Choose a reason for hiding this comment

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

기존 로직을 리팩터링하는 과정에서 올바르게 메세지를 보내기가 어려운 것 같아요.
보니까 충분히 개선점이 있는데 발견하지 못한게 부끄럽네요.
부끄러움을 잊지 않고 꾸준히 노력하겠습니다.

private OrderTables orderTables;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "tableGroup")
private List<OrderTable> orderTables;

Choose a reason for hiding this comment

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

@OneToMany에선 기본값이 LAZY라서 따로 설정해주실 필요는 없어요.

그리고 추가적으로 참고하시면 좋은 링크입니다 https://www.inflearn.com/questions/258175

Copy link
Author

@her0807 her0807 Nov 4, 2022

Choose a reason for hiding this comment

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

지난번에 학습했을 때, @OneToMany 에서는 하나의 객체가 여러개의 객체를 가지고 있기 때문에 가져올 때 즉시 로딩이면 무조건 n+1 이 발생하니까 기본 전략을 지연로딩으로 가져가고, @ManyToOne 일 때는 해당 문제를 고려하지 않아도 되기 때문에 즉시로딩이 기본 전략이라고 이해했던 기억이 나네요!

  • 계속해서 상기한다.
  • 모르는 다른 개발자에게 알린다
    위 두가지 의미로 명시해두었습니다. ㅎㅎ 더즈는 익숙하시군요 부럽습니다.

Comment on lines +68 to +72
final MenuRequest 더즈_떡볶이 = new MenuRequest("더즈_떡볶이", BigDecimal.TEN, 코틀린하는_사람들이_먹는_떡볶이_ID,
Arrays.asList(간장_떡볶이, 쿨피스));

final MenuRequest 스모디_떡볶이 = new MenuRequest("스모디_떡볶이", BigDecimal.TEN, 코틀린하는_사람들이_먹는_떡볶이_ID,
Arrays.asList(매운_떡볶이, 주먹밥));

Choose a reason for hiding this comment

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

재치 있는 테스트 코드들에 박수... 💯

Copy link
Author

Choose a reason for hiding this comment

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

제 코드에 더즈와 함께 했던 추억을 남기고 싶었어요.
프로덕션 코드는 아니지만, 테스트 코드에라도 ,, ㅎㅎㅎㅎ
이런 재미들이 개발을 지속하는데 조금이나마 도움이 된다고 믿습니다. 오늘도 즐거운 개발 😁

@her0807
Copy link
Author

her0807 commented Nov 4, 2022

menu 히스토리 백업에 관하여

이전에 작업을 진행하기 전에는 이벤트를 학습하고 싶어서 이벤트 처리를 고려해보려고 했는데, 시간이 많지 않아서 추후에 다시 한번 도전해보기로 하고, 현재는 제가 할 수 있는 방법으로 두가지 정도 생각해보았습니다.

  1. OrderLineItem 에 컬럼 두개만 추가하기
  2. Menu 에서 필요한 데이터만 추출해서 OrderMenu 객체로 관리하기

첫번째 방법이 좋은 점은 변경점이 최소화 된다는 점이 이었어요. 그러나 나중에 혹시 menu 에서 백업해야하는 데이터가 늘어나거나
백업 데이터에 관련된 추가 로직이 생길 경우 OrderLineItem 에도 변경에 영향이 간다는 점이 아쉬웠어요,.

두번째 방법은 테이블 엔티티를 따로 만들어야해서 조금 번거롭지만, 나중에 변경이 일어날 때 OrderLineItem 과 독립적으로 진행될 수 있다는 점이 좋았어요. 그래서 2번 방법을 택했습니다.

2번 방법을 하다보니 기존에 동작하는 서비스이기 때문에 기존에 설정되어 있던 FK 도 제거해줘야하고 새로 만든 테이블에 대한 외래키 설정도 해줘야하고, 기존에 운영되는 비지니스 로직에도 변경이 일어났어요. 다행인것은 기존에 해왔던 리팩터링 덕분에 해당 패키지 내부에서만 일어나 변경에 유연한 구조임을 느꼈던 것이었습니다.

더즈는 컬럼만 추가하셨던데! 이유가 궁금하네요 ☺️

스크린샷 2022-11-04 오후 7 09 38

Copy link

@ldk980130 ldk980130 left a comment

Choose a reason for hiding this comment

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

마지막 요구 사항까지 잘 마쳐 주셨어요!! 수고하셨습니다ㅎㅎ 상세한 리드미 작성과 설계 과정을 글과 그림으로 자세히 표현해 주셔서 리뷰하면서 저도 많이 배웠습니다.

자잘한 코멘트와 질문을 남겼으니 확인 해주시고 시간 나면 답변도 한 번 달아주세요ㅎㅎ 여러가지로 정신 없는 시기니까 3단계도 이만 머지하겠습니다. 혹시 4단계도 하실 거면 나중에라도 리뷰 요청해주셔도 괜찮으니까 언제든 날려주세요 수고하셨습니다~

Comment on lines +70 to +85
private void validOrderTable(final Long orderTableId) {
if (!orderTableRepository.existsById(orderTableId)) {
throw new CustomIllegalArgumentException(NOT_FOUND_TABLE_EXCEPTION);
}
}

private Order convertSaveConditionOrder(final Long orderTableId, List<OrderLineItem> orderLineItems) {
final OrderTable orderTable = getOrderTable(orderTableId);
return new Order(orderTable, COOKING.name(), LocalDateTime.now(),
orderLineItems);
}

private OrderTable getOrderTable(final Long orderTableId) {
return orderTableRepository.findById(orderTableId)
.orElseThrow(() -> new CustomIllegalArgumentException(NOT_FOUND_TABLE_EXCEPTION));
}

Choose a reason for hiding this comment

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

주문을 만들기 전에 validateOrderTable()로 테이블이 있는지 확인하네요. 그리고 주문을 만들면서 테이블을 가져오는데 없으면 또 예외를 터뜨립니다. 검증이 이중으로 들어가는 것 같은데 getOrderTable()만으로도 충분하지 않을까요?

Comment on lines +21 to +23
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
@JoinColumn(name = "order_menu_id", nullable = false)
private OrderMenu orderMenu;

Choose a reason for hiding this comment

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

더즈는 컬럼만 추가하셨던데! 이유가 궁금하네요 ☺️

메뉴가 변해도 주문 항목이 변하면 안 된다라는 요구 사항을 위해 따로 엔티티로 만들어서 분리하셨군요 👍

네 저는 컬럼만 추가했죠ㅋㅋㅋ 저는 주문 항목이 quantity 속성을 가지고 있는 것 말고는 메뉴와 거의 같다고 생각한 것 같아요. 컬럼만 추가했지만 값 객체로 뺐기 때문에 주문 항목 내부에서 메뉴만의 로직을 따로 처리할 수는 있을 거라 생각했구요.

@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long seq;

@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})

Choose a reason for hiding this comment

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

이건 수달의 생각을 알고 싶은 건데요. 일대일 관계에서 OrderLineItem을 연관관계의 주인으로 선택한 이유가 있을까요?

Comment on lines +45 to +49
System.out.println("ddddd");
System.out.println("ddddd");
System.out.println("ddddd");
final OrderResponse orderResponse = orderService.changeOrderStatus(orderId, request);
System.out.println("ddddd");

Choose a reason for hiding this comment

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

이건 뭔가요?ㅋㅋㅋㅋㅋㅋ

@@ -23,13 +38,18 @@ public TableGroup(final Long id, final LocalDateTime createdDate, final List<Ord
validateTableGroup(orderTables);
this.id = id;
this.createdDate = createdDate;
this.orderTables = new OrderTables(orderTables);
this.orderTables = orderTables;
}

Choose a reason for hiding this comment

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

TableGroup이 생성될 때 OrderTable에는 TableGroup을 넣어주지 않네요. 연관관계 주인이 OrderTable이라 이렇게 되면 연관관계가 안맺어질 것 같아요

Comment on lines +48 to +51
public void ungroup() {
orderTables.forEach(OrderTable::clear);
this.orderTables.clear();
}

Choose a reason for hiding this comment

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

양쪽 다 관계를 끊어주는 것 👍

Comment on lines +36 to +38
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_table_id")
private OrderTable orderTable;

Choose a reason for hiding this comment

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

저도 현재 Order에서 OrderTable이 객체 참조를 하고 있어요. 조영호님의 영상을 보면 객체 참조가 무조건 답은 아니라고 하던데 수달은 어떻게 생각하세요? id를 통한 간접 참조와 객체 참조 중 이 상황에선 어떤 게 나을까요

Comment on lines +82 to +84
public void setOrderLineItems(final List<OrderLineItem> orderLineItems) {
this.orderLineItems = orderLineItems;
}

Choose a reason for hiding this comment

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

테스트에서만 사용하는 setter네요 👀

@ldk980130 ldk980130 merged commit 418e353 into woowacourse:her0807 Nov 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants