Skip to content

[그리디] 이채현 JPA 6단계 제출합니다 - #267

Open
chaehyunL wants to merge 108 commits into
next-step:chaehyunlfrom
chaehyunL:JPA3
Open

[그리디] 이채현 JPA 6단계 제출합니다#267
chaehyunL wants to merge 108 commits into
next-step:chaehyunlfrom
chaehyunL:JPA3

Conversation

@chaehyunL

@chaehyunL chaehyunL commented Jul 18, 2026

Copy link
Copy Markdown

🙋‍♂️인사🙋‍♂️

안녕하세요. 세종대학교 그리디 백엔드 4기 이채현입니다.

이번주는 꽤나 여유로워서 많은 시간을 투자할 수 있을 것 같아요! 많은 피드백 부탁드립니다.!!!


Spring JPA 4-6단계 Entity Manager-> JPA

entity매니저를 사용해서 repository 수정
jpa 전환 및 내 예약 구성코드를 생성함.


고민한 내용 🤔

EntityManager를 JPA로 바꾸려면 어떠한 형식을 가져야 하는가 고민했습니다.

사용해보니, JPA가 EntityManager보다 더 적은 메서드를 쓰고도 충분히 구현되는 것 같습니다.!

실무에서는 JPA를 더 많이 사용하게 될 것 같은데, EntityManager를 사용하게 되는 경우도 있나요?

그리고 Entity에서 단순히 protected를 추가한 것 만으로, 테스트가 돌아가는 경우가 있는데
이 protected의 의미를 잘 모르겠습니다.

@chaehyunL chaehyunL changed the title [그리디] 이채현 JPA4~6단계 제출합니다 [그리디] 이채현 JPA 6단계 제출합니다 Jul 20, 2026
@c0mpuTurtle

c0mpuTurtle commented Jul 22, 2026

Copy link
Copy Markdown

🌱<인사>

안녕하세요 채현님 :)

또 만나뵙게 되어 반갑네요~~ ㅎㅎ
벌써 스터디가 막바지를 향해 달려가고 있는 데요.
채현님은 스스로 성장했다고 느끼고 계신가요?

저도 java부터 spring까지의 여정을 함께 거쳐왔다고 생각하니 감회가 새로운 거 같습니다.
마지막까지 저희 잘 마무리 해봐요!!

채현님께서 시간을 많이 쓸 수 있다고 하셨으니 기대해보겠습니다~


❓<공통 리뷰>

1.

실무에서는 JPA를 더 많이 사용하게 될 것 같은데, EntityManager를 사용하게 되는 경우도 있나요?

실무에서는 Spring Data JPA를 쓰는 경우가 압도적으로 많습니다. 🤓

기본은 Spring Data JPA로 가고, EntityManager는 그 안에서 복잡한 동적 쿼리를 짤 때나 영속성 컨텍스트를 세밀하게 제어해야 할 때 정도만 보조적으로 사용하는 구조가 일반적이에요.
이번 미션에서 EntityManager로 직접 Repository를 구현해본 이유도,
실무에서 이 방식이 더 간편해서가 아니라 Spring Data JPA가 내부적으로 뭘 대신해주고 있는지 이해를 돕기 위한 학습 목적이 커요.!!

저도 실무자가 아니라, 잘은 모르지만 제 체감상은 EntityManager를 직접 사용은 10%, JPA 사용은 90% 정도인 것 같습니다!!

2.

Entity에서 단순히 protected를 추가한 것 만으로, 테스트가 돌아가는 경우가 있는데 이 protected의 의미를 잘 모르겠습니다.

-> 호옥시 어떤 테스트가 protected를 붙이니 돌아가던 가요? 쪼오금 더 부연설명 부탁드립니다.
질문의 맥락을 정확히 파악하기가 살짝 어렵네요 😅

우선 protected는 "동일한 패키지 내의 클래스" 또는 "해당 클래스를 상속받은 자식 클래스"에서만
접근을 허용하는 Java의 접근 제어자입니다.

기본적으로 접근 제어자는 외부의 잘못된 접근으로부터 객체의 내부 상태를 보호하고,
필요한 데이터 부분만 공개하기 위해 만들어졌습니다.

Screenshot 2026-07-23 at 12 38 34 AM

어떤 테스트 상황이었는지 에러 메시지나 관련 코드를 공유해 주시면,
왜 protected를 추가했을 때 테스트가 통과했는지 더 정확히 답변해 드릴 수 있을 것 같아요!

(1, '', '2024-03-01', 3, 3);

INSERT INTO reservation (name, date, time_id, theme_id)
VALUES ('브라운', '2024-03-01', 1, 2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@joincolumn(nullable=false) 선언이 없어서 JPA DDL은 member_id를 nullable로 만들어져 있죠.
이 데이터가 적재되어 있을 때 findAll()을 조회하면 어떤 에러가 발생할까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

findAll() 자체는 정상적으로 조회되지만, member의 정보를 조회할때 NuLLpoionterException을 겪을 수 있습니다

Comment on lines +43 to +44
Member member = memberRepository.findByName(name)
.orElseThrow();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ReservationDao.findByMemberName()이 WHERE r.name = :name 으로 조회하는데,
name은 유니크한 값이 아니라서, 동명이인이 존재할 경우 두 사람의 예약이 함께 반환됩니다.

예를 들어 "김철수"라는 이름의 계정이 두 개라면,
한 사람이 /reservations-mine을 조회할 때 다른 사람의 예약도 노출됩니다. !!

이를 어떻게 해결하면 좋을까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

처음에는 ReservationService를 사용해서,
save 메서드에서 name대신 memberId를 조회하는 방식으로 쓰려고 햇으나 기존 member 저장 방식 때문에 다른 name도 불러오게 되는 오류가 발생했습니다.

그 대신,
reservation-mine을 조회할때 member Id로 예약을 불러오는 방식으로 해결했습니다.

Comment on lines +6 to +14
private final Long id;
private final Member member;
private final String theme;
private final String date;
private final String time;
private final String name;

public ReservationResponse(Member member, String name, Long id, String theme, String date, String time) {
this.member = member;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

DTO가 객체를 가지고 있을 때 생길 수 있는 문제점들은 어떤 것들이 있을까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

보안상의 문제가 있을 수도 있다고 생각합니다.

Comment on lines +10 to 17
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;
private String password;
private String role;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

DB 안에는 email의 unique 제약이 있는 데 엔티티에는 따로 제약사항이 없네요.

이렇게 DB와 엔티티에 제약사항이 다를 경우 생길 수 있는 문제들은 어떤 것들이 있을까요?

@chaehyunL chaehyunL Jul 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Member Entity에서는 여러개의 email을 넣었을때는 비즈니스적으로는 문제가 발생하지
않지만,

DB 내에서는 unique 제약이 걸려있어서 그 부분에서 충돌이 일어날 수 있을 것 같습니다.

DB안에 unique 제약이 걸려있다고 하셨는데, 어떤 패키지를 통해 확인할 수 있나요?

Comment on lines +11 to 15
import roomescape.member.MemberRepository;

@Component
public class LoginInterceptor implements HandlerInterceptor {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

  1. admin을 확인하는 과정에서 Filter를 대신 인터셉터를 사용하신 이유가 있을까요?
  2. Filter란 무엇일까요?
  3. Interceptor란 무엇일까요?
  4. Filter와 Interceptor는 각각 어떤 상황에서 쓰일까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

필터(Filter)

스프링 외부에서 관리

웹 애플리케이션 전체 적용

주요 용도. 인코딩 변환, xss 방어, cors 설정 등

인터셉터

스프링 컨테이너 내부

특정 url 및 컨트롤러 기준

주요 용도. 로그인 체크, 권한 부여,api 호출 로깅, 실행시간 계산 등..


admin 관리자임을 확인하고 권한 부여한다고 생각해서

인터셉터가 더 적합하다고 생각되었습니다.

Comment on lines 28 to 45

@c0mpuTurtle c0mpuTurtle Jul 22, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 get 메서드를 대신할 수 있는 어노테이션을 사용해볼까요?

Comment on lines 19 to 20
protected Member(){
}

@c0mpuTurtle c0mpuTurtle Jul 22, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 생성자 메서드를 대신할 수 있는 어노테이션을 사용해볼까요?

Comment on lines +8 to +19
public class LoginMember {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String password;
private String role;

protected LoginMember() {

}

@c0mpuTurtle c0mpuTurtle Jul 22, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

DTO의 역할을 하는 객체인 거 같은 데 ID를 붙여준 이유가 있나요?
이 객체는 DB에 저장되나요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

entity라고 착각해서,id를 붙여준 것 같습니다.
이 객체는 db에 저장되지 않으므로, @id를 빼는게 나을 것 가타요

Comment on lines 17 to -18
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
private final MemberDao memberDao;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ArgumentResolver를 만드신 이유가 무엇인가요?
어떤 걸 편리하게 하기 위함이죠??

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

로그인하는 멤버를 잡아서, 로그인을 확인하고 admin 권한을 가지고 있는 사람에게 권한을 부여해줍니다


import roomescape.member.Member;

public class ReservationResponse {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

채현님께서 어떤 DTO는 class 자료형을 사용하고 어떤 DTO는 record자료형을 사용하고 계신 거 같아요.
class DTO와 record DTO를 정하신 채현님만의 기준이 있으신가요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

아직 무엇이 더 좋을지 몰라서, 둘다 써보고 있습니다.

혜빈 리뷰어님은 어떤게 더 좋은 것 같나요?

제가 느끼기에는 entity에는 class를 쓰는게 맞는 것 같은데 response와 같이 단순 dto면 다 record로 바꿔도 상관 없는 것 같다고 느껴져요

private final ThemeRepository themeRepository;
private final TimeRepository timeRepository;
private final MemberRepository memberRepository;
private final WaitingRepository waitingRepository;

@c0mpuTurtle c0mpuTurtle Jul 22, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WaitingRepository가 없어서 컴파일이 안 되는 데 혹시 서비스 테스트를 어떻게 하셨나용...?
파일 넣어주셔야 할 거 같아요!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

로컬에서 푸시 안했습니다 확인하고 푸시했습니다

Comment on lines +15 to +25
public Theme create(Theme theme){
return themeRepository.save(theme);
}

public void delete(Long id){
themeRepository.deleteById(id);
}

public List<Theme> findAll(){
return themeRepository.findAll();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

JPA(지난 번 미션)와 비교해서 Spring Data JPA는 어떤 점들이 편리했나요?
코드를 구현하시면서 어떤 점들이 보다 더 편리하다고 느꼈는 지 채현님의 의견이 궁금해욥!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Entity Manager의 경우에는 일일히 모든 것들을 입력해줘야해서 조금 더 손이 가는 느낌이였더라면, JPA는 일정한 것들을 대신해주니까 매우 편리하다는 느낌이 들었어요!

Comment on lines -5 to +13
private String value;
private String timeValue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

timeValue로 필드명을 변경하신 이유가 있나요??

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

value라고 했을때, 어떠한 value인지 불명확하다고 생각되어서, 직관성이 부족하다는 느낌이 들었습니다.
timeValue이면 시간에 관한 value임을 알 수 있을 것 같아 이렇게 변경하게 되었습니다.

Comment on lines +36 to +41
public Waiting(Member member, String date, Theme theme, Time time) {
this.member = member;
this.date = date;
this.theme = theme;
this.time = time;
}

@c0mpuTurtle c0mpuTurtle Jul 22, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Lombok의 @builder 키워드에 대해 알아보시는 것도 좋을 것 같아요!
가독성 향상이나 생성자 파라미터 순서 실수를 줄이는 데 큰 도움이 되니 프로젝트 들어가기 전에 한 번 알아보깅~~

Comment on lines +21 to +31
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;

@ManyToOne
@JoinColumn(name = "theme_id")
private Theme theme;

@ManyToOne
@JoinColumn(name = "time_id")
private Time time;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

  • FK값을 Waiting에 두신 이유가 있나요?
  • @manytoone 어노테이션의 역할은 무엇이죠?
  • 이 어노테이션이 붙었을 때 데이터베이스 테이블표는 어떻게 구성되나요?

@chaehyunL chaehyunL Jul 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

FK 값을 Waiting에 두신 이유가 있나요?

Fk는 많은 데이터 테이블을 가질때 일반적으로 쓰입니다/

ex. 회원 한명은 여러 개의 대기 신청을 할 수 있음.등..

에서 알 수 있습니다.

@manytoone 어노테이션의 역할은 무엇인가요?

여러 개의 waiting이 하나의 member를 참조

여러개의 waiting이 하나의 theme을 참조

등.. 을 의미합니다

이 어노테이션이 붙었을때 데이터베이스 테이블표는 어떻게 구성되나요?

FK를 포함한 데이터 테이블이 생성될 것 같습니다

@c0mpuTurtle

c0mpuTurtle commented Jul 22, 2026

Copy link
Copy Markdown

#266 (comment)
위의 리뷰에서 아래와 같이 남겨주셨는 데 여기다 답변 적어주시면 좋을 거 같아요!!

한 번 호출해서 예외를 확인해보시면 좋을 것 같아요~!

어떤 예외 메시지가 발생하나요?
Dao 호출 때문에 회원가입이 이루어지지 않는다고 판단하신 근거가 궁금해요!
수정 후 해결 방법도 함께 작성해주세요~~

WaitingRepository 추가 하신 뒤에 회원가입이 잘 동작하는 지 확인해주시면,
그 이후에 구조적으로 문제는 없는 지에 대해서 저도 코드 확인해보겠습니다!!

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