Skip to content

Conversation

@nonactress
Copy link

@nonactress nonactress commented Nov 13, 2025

안녕하세요! 백경환 리뷰어님
스프링은 처음이라 많이 서툴었습니다. 따끔하게 리뷰해주시면 열심히 배워보겠습니다!

일단 전반적인 코드 흐름을 설명해보겠습니다.

컨트롤러 (Controller)

  • AdminController: @Controller를 사용하여 웹 페이지를 반환합니다. 사용자가 특정 경로로 접속했을 때, 해당하는 HTML 파일을 렌더링하여 보여줍니다.
    • /: home.html (홈 페이지)
    • /reservation: reservation.html (예약 관리 페이지)
  • ReservationController: @RestController를 사용합니다. HTTP 요청을 받아 ReservationService를 호출하고, 그 결과를 JSON 형태로 반환합니다.
    • GET /reservations: 모든 예약 목록을 조회합니다.
    • POST /reservations: 새로운 예약을 생성합니다.
    • DELETE /reservations/{id}: 특정 예약을 삭제합니다.

서비스 (Service)

  • ReservationService: 핵심 비즈니스 로직을 담당합니다.
    • 예약 데이터를 List<Reservation> 형태로 메모리에 저장하고 관리합니다.
    • AtomicLong을 사용하여 예약 ID를 순차적으로 생성합니다.
    • 주요 메서드:
      • getAllReservations(): 전체 예약 목록을 반환합니다.
      • addReservation(): 새로운 예약을 리스트에 추가하고, 생성된 예약 객체를 반환합니다.
      • deleteReservation(): ID를 기준으로 예약을 찾아 리스트에서 삭제합니다. 존재하지 않는 ID일 경우 IllegalArgumentException을 발생시킵니다.

궁금한점

  • 스프링의 컨테이너가 String constant pool 같은건가요?
    @service로 만든 서비스 클래스는 어디서나 같은 static 같다고 느껴졌습니다. 그래서 Java 미션을 하며 string constant pool 이라는 곳에서 String이 관리 된다고 공부한 적이 있어 비슷한 개념인가 궁금합니다.
  • 테스트를 위한 코드
    삼단계 테스트를 통과하기 위해 지금 Service를 보시면 clear()을 만들어 reservations/3을 초기화 하는 방식으로 테스트를 통과하였습니다. claer()를 사용하지 않고 요구사항을 만족 시킬려면 어떤 방식으로 리팩토링 해야 하는지 궁금합니다.

Copy link

@dooboocookie dooboocookie left a comment

Choose a reason for hiding this comment

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

안녕하세요, 현진.
리뷰어 루카입니다.

이번 리뷰는 어떤 방식으로 리뷰를 드릴지 조금 고민이되네요.

질문에 대한 답부터 먼저 드리겠습니다.

스프링의 컨테이너가 String constant pool 같은건가요?
@service로 만든 서비스 클래스는 어디서나 같은 static 같다고 느껴졌습니다. 그래서 Java 미션을 하며 string constant pool 이라는 곳에서 String이 관리 된다고 공부한 적이 있어 비슷한 개념인가 궁금합니다.

다른 개념입니다.

테스트를 위한 코드
삼단계 테스트를 통과하기 위해 지금 Service를 보시면 clear()을 만들어 reservations/3을 초기화 하는 방식으로 테스트를 통과하였습니다. claer()를 사용하지 않고 요구사항을 만족 시킬려면 어떤 방식으로 리팩토링 해야 하는지 궁금합니다.

지금 프로덕트 코드에서는 딱히 방법이 떠오르지 않습니다.
구체적인 내용은 해당부분에 코멘트로 달아놨습니다.


추가적인 요청

  • Service의 책임을 고려해보셨으면 좋겠습니다.
  • 개행, 들여쓰기 같은 기본 코드 포맷을 잘 맞춰주세요.
  • 질문은 조금 더 명확히 해주시면 좋겠습니다.

아무래도 스프링 MVC 미션을 할 때 모두들 조금 어려워하는 경향이 있는데,
조금 더 본인이 이 미션을 통해서 어떤것을 학습해야할지 고민해보셨으면 좋겠습니다.
그를 혼자 파보고 궁금증이 생겼을 때 저한테 질문을 주시면 더 좋은 리뷰가 될 수 있을 것 같습니다.

Comment on lines 4 to 20

Choose a reason for hiding this comment

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

👀 Comment

Reservation과 ReservationResponse는 어떤 개념적인 차이가있을까요?
필드과 완전히 동일한데, 굳이 따로있어야하는 이유는 어떤것일까요?

Copy link
Author

Choose a reason for hiding this comment

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

모델과 dto의 차이라고 생각합니다.

Reservation : 모델
ReservationResponse : dto
생각해보면 dto로 넘겨줘야 하는 인자들은 변할 수 있습니다. 그에 맞춰 dto를 만들어서 넘겨주면 되지만 만약 Reservation만 코드 상에 존재하게 되어 dispatcherservlet에게 넘겨 줘야 하는데 그에 맞춰 메서드를 만들거나 하면서 모델의 역할인 내부 비즈니스 로직과 DB 상태를 처리하는 것 보다 더 많은 책임을 가지는 것 같습니다.

Comment on lines 7 to 8

Choose a reason for hiding this comment

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

🔥 Request Change

String은 너무 자유도가 높은 타입입니다.
누군가는 hh시 mm분, 누군가는 hh:mm이라고 입력할수 있겠죠.

java에는 LocalDate와 같은 날짜나 시간을 나타내는 자료형이 있습니다.
조금 더 명환한 타입을 찾아서 선택해주세요

Copy link
Author

Choose a reason for hiding this comment

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

LocalTime과 LocalDate로 자료형 바꿨습니다.

Copy link
Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

👀 Comment

ID 자동증가생성을 위해 counter를 1씩 올리면서 사용중인 것 같은데요.
지금은 Memory에 데이터를 저장하고 있는데요.

만약에 DB와 같은 저장소에 데이터를 저장하는 방식으로 바꿔야한다는 요구사항이 생기면,
이 부분에 코드 변화가 생기겠네요.
심지어 이를 사용하는 비즈니스 로직에도요.

이는 이 부분이 과연 현진이 PR에 서비스에 핵심 비즈니스 로직을 구현했다고 말씀하셨는데요.
ID를 생성하는 것이 핵심 비즈니스로직이라고 할 수 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

리뷰어님의 말씀을 들어보니 단순 id를 생성이 비즈니스 로직보단 DB에서 자동적으로 일어나는 과정이라는 생각이 듭니다. repository로 따로 관리하도록 코드를 리팩토링해보겠습니다.

Copy link
Author

Choose a reason for hiding this comment

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

Comment on lines 48 to 50

Choose a reason for hiding this comment

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

👀 Commente

테스트를 위한 코드
삼단계 테스트를 통과하기 위해 지금 Service를 보시면 clear()을 만들어 reservations/3을 초기화 하는 방식으로 테스트를 통과하였습니다. claer()를 사용하지 않고 요구사항을 만족 시킬려면 어떤 방식으로 리팩토링 해야 하는지 궁금합니다.

글쎄요.
저는 service에 서비스 정책과 무관한 로직이 있는 것이 적절치않다고 생각합니다.
지금 현재 프로덕트 코드에서 이를 해결할 수 있는 방법이 명확하게 떠오르지도 않구요.

저는 그래서 이 질문에 대해서는 2가지 의견입니다.

  1. 테스트가 매회 동일한 결과를 반환하려면 clear는 필요할 수 있다, 하지만 그것이 service에 있어야할 내용인지는 모르겠다.
  2. 테스트에서 아이디를 정적으로 1인지 2인지 확인하는 방식은 좋지않은 테스트 같다.

Copy link
Author

@nonactress nonactress Nov 16, 2025

Choose a reason for hiding this comment

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

  1. 테스트가 매회 동일한 결과를 반환하려면 clear는 필요할 수 있다, 하지만 그것이 service에 있어야할 내용인지는 모르겠다.

Service가 가지고 있던 arrayListrepository로 분리를 하면서 clear()repository가 하도록 코드를 리팩토링 하였습니다.

  1. 테스트에서 아이디를 정적으로 1인지 2인지 확인하는 방식은 좋지않은 테스트 같다.
    리뷰어님이 말씀하신 내용이 테스트 코드의 존재 목적이 뭔지 생각을 해보게 하는 것 같습니다.

테스트 코드는 핵심 비즈니스 로직을 확인해본다고 생각합니다. 그에 반한 지금의 테스트는 id를 잘 구성해놓았나를 확인해본다는 관점에서 말씀하신 것 같습니다. 그런 방면에서 본다면 좋지 않은 테스트 코드 인 것 같습니다. (다만 위 테스트코드가 제가 작성한 것이 아닌 초록스터디에서 주어진 것 수정해도 되는 지 잘 모르겠습니다.)

@nonactress
Copy link
Author

Service의 책임을 고려해보셨으면 좋겠습니다.

말씀해주신 리뷰를 읽다 보니 Spring mvc에 대한 이해가 낮은 거 같아 조금 더 공부해보게 되었습니다. 서비스의 책임이란 비즈니스 '규칙'을 결정하고 '처리'한다 라고 생각합니다. 서비스는 컨트롤러와 레포지토리 사이에서 요청이 들어오면 어떤 데이터를 가지고 어떻게 사용자가 원하는 결과에 도달할 수 있는지를 결정하는 계층이라고 생각합니다. 그래서 요번 리뷰 반영 전 코드에서 위 책임을 생각해보면 서비스가 배열을 가지고 있는 것은 사실 아주 큰 위반사항입니다. 실제론 db가 되겠지만 지금은 배열로 생성해서 사용하는 코드를 서비스가 가지지 않고 레포지토리가 가지도록 리팩토링 해보았습니다. ( 여기서 사실 db가 데이터를 가지고 있고 서비스에서 어떤 데이터에 대한 요청을 서비스가 하면 이에 맞는 데이터를 레포지토리가 db에서 찾아서 넘겨준다는 것은 알지만 지금 arrayList로 데이터를 관리하고 있어 레포지토리에 넣어놨습니다. ) 공부하며 흥미로운 주제를 찾아보았습니다.

풍부한 도메인 모델 vs 빈약한 도메인 모델

풍부한 도메인 모델이 좋은 패턴이고 빈약한 도메인 패턴이 안티 패턴이라고 설명을 하고 있었습니다. 여기서 두 패턴을 나누는 기준은 비즈니스 로직의 위치였습니다. 풍부한 도메인 모델은 비즈니스 로직이 도메인 모델에 존재하고 빈약한 도메인 모델은 서비스에 비즈니스 로직이 존재하였습니다. 그래서 서비스는 모델에 있는 비즈니스 로직을 사용하면 되고 oop관점에서도 좋고 캡슐화도 잘된다 라고 설명했습니다.

그럼 서비스 계층이 비즈니스 로직을 가지면 안된다는 건가?

저는 여기서 혼란에 빠지기 시작했습니다. 어떤 자료에선 비즈니스 로직이 도메인 모델에 있어야 한다고 하고, 다른 곳에서는 서비스 계층의 목적은 컨트롤러에서 요청을 받아서 비즈니스 로직을 수행하는 것 이라고 하니 혼돈이 있었습니다. 그래서 왜 서비스 계층에 비즈니스 로직을 설계 해야 하나를 고민해보니 저번 스터디에서 배운 @transactional 같은 기능이 서비스에 존재하지 않는다면 동시에 데이터를 바꾸게 되어 원하는 비즈니스 로직을 수행하지 못할 수 도 있겠구나 생각했습니다. 이에 대해 리뷰어님은 왜 도메인 모델이 아닌 서비스 계층에서 비즈니스 로직을 처리해야하는지 묻고 싶습니다.

스프링의 컨테이너가 String constant pool 같은건가요?
@service로 만든 서비스 클래스는 어디서나 같은 static 같다고 느껴졌습니다. 그래서 Java 미션을 하며 string constant pool 이라는 곳에서 String이 관리 된다고 공부한 적이 있어 비슷한 개념인가 궁금합니다.

처음에 질문했던 저의 의도는 다음과 같습니다. 자바 미션에서 String name = "asdf"; 와 String name1 = "asdf"가 같은 주소값을 가지게 된다고 배웠습니다. 이 원리는 문자열 상수 풀 (String Constant Pool)이 만약 지금 저장하려는 값이 존재한다면 같은 주소를 공유하게 하는 원리라고 이해하고 있습니다.
이와 같이 Spring bean에서 service를 생성시켜 놓으면 @Autowired를 통해 같은 인스턴스를 사용하니 비슷원리로 돌아가는 건가 하고 질문을 드렸습니다.

개행, 들여쓰기 같은 기본 코드 포맷을 잘 맞춰주세요.

다음엔 한번 더 확인하고 푸쉬 하겠습니다.

질문은 조금 더 명확히 해주시면 좋겠습니다.

제 질문이 다소 추상적이게 느껴졌을 수 도 있다고 생각했습니다. 최대한 풀어서 작성해보겠습니다.

LocalDate,LocalTime이 왜 적용이 되지 않을까?

리뷰 남겨 주신 자료형인 LocalTimeLocalDate로 리팩토링 후 실행 시켜 보니 post 가 안되게 되었습니다. 그래서 이유를 조사해보니 LocalTimeLocalDate는 jacson이 "YYYY-MM-DD"와 "HH:MM"와 같은 문자열이 아니라 다른 방식으로 넘겨온 데이터를 직렬화를 할 수 없어서 라고 해서 다시 String으로 바꿨습니다.

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.

2 participants