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

[Spring Data JPA] 안금서 미션 제출합니다. #110

Open
wants to merge 50 commits into
base: goldm0ng
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
5913de1
<ADD> gradle 의존성 추가
goldm0ng Dec 26, 2024
67da617
<ADD> 토큰 기반 인증 방식 jwt 관련 기능 추가
goldm0ng Dec 26, 2024
53828d8
<FIX> MemberRequest DTO를 record 형식으로 변경
goldm0ng Dec 26, 2024
0ec5186
<ADD> jwt를 사용한 로그인 및 인증 정보 조회 기능 구현
goldm0ng Dec 26, 2024
ebf6407
<ADD> 로그인 사용자 정보인 MemberAuthInfo를 조회하는 HandlerMethodArgumentResolver 구현
goldm0ng Dec 26, 2024
22f9e30
<FIX> HandlerMethodArgumentResolver 구현에 따른 예약 API 및 기능 리팩터링
goldm0ng Dec 26, 2024
347d0c7
<ADD> 관리자 기능 구현
goldm0ng Dec 26, 2024
2a96836
<ADD> 사용자의 정보를 조회하는 ArgumentResolver와 관리자만 해당 경로에 접근할 수 있도록 하는 Interc…
goldm0ng Dec 26, 2024
91bf7bf
<ADD> 예외처리 추가
goldm0ng Dec 26, 2024
16e217b
<ADD> 1 ~ 3단계 미션 테스트 추가
goldm0ng Dec 26, 2024
4a9ff8c
<FIX> DTO record 타입으로 변경
goldm0ng Jan 7, 2025
a5e8d66
<FIX> DTO record 타입으로 변경에 따른 수정
goldm0ng Jan 7, 2025
e027b31
<FIX> 응답 바디 타입 수정
goldm0ng Jan 7, 2025
c7a1875
<FIX> 인증 패키지 구조 변경
goldm0ng Jan 8, 2025
2358568
<FIX> JwtUtils 관련 수정 (멤버 변수 및 메서드)
goldm0ng Jan 8, 2025
b44dab1
<FIX> 에러 추적 용이하도록 에러 포함
goldm0ng Jan 8, 2025
330b40c
<FIX> gradle 의존성 jdbc -> jpa로 대체
goldm0ng Jan 8, 2025
7a7ed55
<ADD> JPA 관련 설정 추가
goldm0ng Jan 8, 2025
c5f4f97
<ADD> 초기 데이터베이스 seed 데이터 추가 및 기존 스키마 삭제
goldm0ng Jan 8, 2025
580c6ab
<FIX> Time 도메인 엔티티 매핑 및 JPA 전환
goldm0ng Jan 8, 2025
edfbc2c
<FIX> Theme 도메인 엔티티 매핑 및 JPA 전환
goldm0ng Jan 8, 2025
312bdea
<FIX> Reservation 도메인 엔티티 매핑 및 JPA 전환
goldm0ng Jan 8, 2025
bef9160
<FIX> Member 도메인 엔티티 매핑 및 JPA 전환
goldm0ng Jan 8, 2025
f164ce0
<FIX> JPA 전환에 따른 LoginService 수정 및 예외처리 방식 변경
goldm0ng Jan 8, 2025
e2a32bc
<ADD> 4단계 테스트 코드 추가
goldm0ng Jan 8, 2025
9c5e253
<FIX> Spring MVC (인증) 미션 테스트 이름 수정
goldm0ng Jan 8, 2025
0840983
<FIX> 초기값 설정을 위한 쿼리 수정
goldm0ng Jan 9, 2025
da71532
<FIX> 회원 인증 정보 DTO 수정 (id 정보 추가)
goldm0ng Jan 9, 2025
84f4e41
<ADD> 내 예약 목록 응답 DTO 추가
goldm0ng Jan 9, 2025
fd7a976
<FIX> Reservation - Member 추가 연관관계 매핑
goldm0ng Jan 9, 2025
fcf74d3
<FIX> Reservation DTO를 record 타입으로 변경
goldm0ng Jan 9, 2025
54d412e
<ADD> 내 예약 목록 조회 기능 구현
goldm0ng Jan 9, 2025
a4b3705
<FIX> JwtUtils id 추출 코드 수정
goldm0ng Jan 9, 2025
b8d792b
<ADD> 사용자 정의 예외 생성 및 핸들러에 추가
goldm0ng Jan 9, 2025
aba712d
<ADD> 5단계 테스트 코드 추가 및 전 단계 테스트 수정
goldm0ng Jan 9, 2025
d472034
<ADD> 예약 대기 요청 및 취소 기능 구현
goldm0ng Jan 9, 2025
751587d
<ADD> 예약 중복 방지 및 내 예약 조회 시 예약 대기 목록까지 보이도록 구현
goldm0ng Jan 9, 2025
e963234
<ADD> 중복 예약 관련 커스텀 예외 추가
goldm0ng Jan 9, 2025
8b292c3
<ADD> 6단계 테스트 코드 추가 및 테스트 깨지는 부분 수정
goldm0ng Jan 9, 2025
39a0f7d
<FIX> 예약 대기 취소 구현 오류 해결
goldm0ng Jan 13, 2025
e7e5c7b
<FIX> 공통 예외 처리 핸들러 중복 코드 제거
goldm0ng Jan 13, 2025
a80573b
<FIX> 예외 처리 오류 2차 해결 및 로깅 방식 수정
goldm0ng Jan 14, 2025
08017a7
<FIX> JPA 쿼리 메소드 적용
goldm0ng Jan 14, 2025
35ec5f7
<FIX> 기본 생성자 접근지정자 변경 및 생성 어노테이션 추가
goldm0ng Jan 14, 2025
1959159
<ADD> 변경에 유연하도록 인터페이스 추가 및 jwt 인증 구현체 추가
goldm0ng Jan 10, 2025
b4bf7fb
<FIX> 기존 JwtUtils를 클래스 속성에 맞도록 이름 수정
goldm0ng Jan 10, 2025
c74ea76
<ADD> jwt 인증 로직을 모아놓은 service 추가
goldm0ng Jan 10, 2025
c4d0384
<FIX> 인증 응답 이름 더 포괄적으로 수정
goldm0ng Jan 10, 2025
a25995f
<FIX> jwt 패키지와의 강한 결합 분리
goldm0ng Jan 10, 2025
4c8b450
Merge branch 'goldm0ng' into spring-data-jpa
goldm0ng Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package roomescape.authentication;

public record MemberAuthInfo(
Long id,
String name,
String role) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ public MemberAuthInfo extractMemberAuthInfoFromToken(String token) {
.parseClaimsJws(token)
.getBody();

Long id = Long.valueOf(claims.getSubject());
String name = claims.get("name", String.class);
String role = claims.get("role", String.class);

return new MemberAuthInfo(name, role);
return new MemberAuthInfo(id, name, role);
} catch (JwtException e) {
throw new JwtValidationException("유효하지 않은 JWT 토큰입니다.", e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.exception;

public class DuplicateReservationException extends RuntimeException {
public DuplicateReservationException(String message) {
super(message);
}
}
20 changes: 11 additions & 9 deletions src/main/java/roomescape/exception/GeneralExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
package roomescape.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class GeneralExceptionHandler {

@ExceptionHandler(MemberNotFoundException.class)
public ResponseEntity<String> handleMemberNotFound(MemberNotFoundException e) {
@ExceptionHandler({MemberNotFoundException.class, JwtValidationException.class, JwtProviderException.class})
public ResponseEntity<String> handleMemberNotFound(Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}

@ExceptionHandler(JwtValidationException.class)
public ResponseEntity<String> handleJwtValidationException(JwtValidationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
@ExceptionHandler({TimeNotFoundException.class, ThemeNotFoundException.class})
public ResponseEntity<String> handleTimeNotFound(Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
}

@ExceptionHandler(JwtProviderException.class)
public ResponseEntity<String> handleJwtProviderException(JwtProviderException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
@ExceptionHandler(DuplicateReservationException.class)
public ResponseEntity<String> handleDuplicatedReservation(DuplicateReservationException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}

@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralException(Exception e) {
e.printStackTrace();
log.error("Exception [Err_Location] : {}", e.getStackTrace()[0]);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());

Choose a reason for hiding this comment

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

보통
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
와 같은 진짜 뭔지 모르는 곳에서 터지는 에러의 경우에는 e.getMessage() 가 서버의 중요한 부분을 담고 있을 수도 있어서 이런 케이스에서는 그냥 body 에 "잠깐 문제가 생겼어요. 다음에 다시 시도해주세요" 같은 문구를 내려주는 편입니다!

Copy link
Author

Choose a reason for hiding this comment

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

아하 그렇군요!
Q1. 예를 들면 어떤 중요한 부분 말씀이신가요? 감이 잘 잡히지 않네요!
Q2. 그리고 누누님은 예외 응답을 내려줄 때 중대한 문제를 일으킨 경험이 있으신가요? 있다면 어떤 문제였고, 어떻게 해결하셨는지도 궁금합니다! (꼭 예외 응답을 내려줄 때가 아니어도 예외처리 관련한 경험이 있으시다면 말씀해주세요!)

Copy link

@be-student be-student Jan 15, 2025

Choose a reason for hiding this comment

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

Q1 > 서버에 어떤 라이브러리를 사용하고 있는지와 같은 정보들이 전달되는 것 자체가 문제가 될 수 있어요!
Q2 > 저는 예외 응답을 만들다가 예외가 터졌던 기억이 나네요
근데, 예외 응답을 만드는 곳도(catch 문) 다시 한번 감싸져서 기본 응답을 주도록 되어있어서 문제 없이 동작했던 경험이 있어요!

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package roomescape.exception;

public class MemberNotFoundException extends RuntimeException {
public MemberNotFoundException(String message, Throwable cause) {
super(message, cause);

public MemberNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.exception;

public class ThemeNotFoundException extends RuntimeException {
public ThemeNotFoundException(String message) {
super(message);
}
}
7 changes: 7 additions & 0 deletions src/main/java/roomescape/exception/TimeNotFoundException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.exception;

public class TimeNotFoundException extends RuntimeException {
public TimeNotFoundException(String message) {
super(message);
}
}
26 changes: 12 additions & 14 deletions src/main/java/roomescape/login/LoginService.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
package roomescape.login;

import lombok.RequiredArgsConstructor;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import roomescape.authentication.MemberAuthInfo;
import roomescape.authentication.AuthenticationResponse;
import roomescape.authentication.AuthenticationService;
import roomescape.member.Member;
import roomescape.member.MemberDao;

import roomescape.exception.MemberNotFoundException;
import roomescape.member.MemberRepository;

@Service
@RequiredArgsConstructor
public class LoginService {

private final MemberDao memberDao;

private final MemberRepository memberRepository;
private final AuthenticationService authenticationService;

public AuthenticationResponse login(LoginRequest loginRequest) {

try {
Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password());
Member member = memberRepository.findByEmailAndPassword(loginRequest.email(), loginRequest.password())
.orElseThrow(() -> new MemberNotFoundException("입력한 이메일 혹은 비밀번호로 가입한 회원을 찾을 수 없습니다."));

return authenticationService.createToken(member);
} catch (EmptyResultDataAccessException e) {
throw new MemberNotFoundException("이메일 혹은 비밀번호가 맞지 않습니다.", e);
}
}

public LoginCheckResponse checkLogin(MemberAuthInfo memberAuthInfo) {

try {
Member member = memberDao.findByName(memberAuthInfo.name());
return new LoginCheckResponse(member.getName());
} catch (EmptyResultDataAccessException e) {
throw new MemberNotFoundException("로그인이 되지 않은 상태입니다.", e);
}
Member member= memberRepository.findByName(memberAuthInfo.name())
.orElseThrow(() -> new MemberNotFoundException("로그인 된 회원이 아닙니다."));

return new LoginCheckResponse(member.getName());
}
}
16 changes: 16 additions & 0 deletions src/main/java/roomescape/member/Member.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
package roomescape.member;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {

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

private String name;

private String email;

private String password;

private String role;

public Member(Long id, String name, String email, String role) {
Expand Down
55 changes: 0 additions & 55 deletions src/main/java/roomescape/member/MemberDao.java

This file was deleted.

12 changes: 12 additions & 0 deletions src/main/java/roomescape/member/MemberRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package roomescape.member;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findByEmailAndPassword(String email, String password);

Optional<Member> findByName(String name);
}
9 changes: 4 additions & 5 deletions src/main/java/roomescape/member/MemberService.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package roomescape.member;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MemberService {
private MemberDao memberDao;

public MemberService(MemberDao memberDao) {
this.memberDao = memberDao;
}
private final MemberRepository memberRepository;

public MemberResponse createMember(MemberRequest memberRequest) {
Member member = memberDao.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER"));
Member member = memberRepository.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER"));
return new MemberResponse(member.getId(), member.getName(), member.getEmail());
}
}
10 changes: 10 additions & 0 deletions src/main/java/roomescape/reservation/MyReservationResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package roomescape.reservation;

public record MyReservationResponse(
Long reservationId,
String theme,
String date,
String time,
String status
) {
}
38 changes: 31 additions & 7 deletions src/main/java/roomescape/reservation/Reservation.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
package roomescape.reservation;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import roomescape.member.Member;
import roomescape.theme.Theme;
import roomescape.time.Time;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Reservation {

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

private String name;

private String date;

@ManyToOne
@JoinColumn(name = "time_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Time time;

@ManyToOne
@JoinColumn(name = "theme_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Theme theme;

public Reservation(Long id, String name, String date, Time time, Theme theme) {
this.id = id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;

public Reservation(Member member, String name, String date, Time time, Theme theme) {
this.member = member;
this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
}

public Reservation(String name, String date, Time time, Theme theme) {
this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
}

public Reservation() {

}

public Long getId() {
return id;
}
Expand All @@ -48,4 +68,8 @@ public Time getTime() {
public Theme getTheme() {
return theme;
}

public Member getMember() {
return member;
}
}
Loading