Skip to content
Open
Show file tree
Hide file tree
Changes from 98 commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
d0eac68
test: 1단계,2단계 추가
chaehyunL Jun 28, 2026
53bba1c
feat:LoginController 생성
chaehyunL Jun 28, 2026
24ee8ee
feat:LoginRequest 생성
chaehyunL Jun 28, 2026
54aee6d
feat: findById 메서드 추가
chaehyunL Jun 28, 2026
0365054
feat: LoginService 생성
chaehyunL Jun 28, 2026
143eb7d
feat: JwtProvider 생성
chaehyunL Jun 29, 2026
ecc8025
feat: LoginMember 생성
chaehyunL Jun 29, 2026
80053ba
feat: LoginMember 핸들러 생성
chaehyunL Jun 29, 2026
ab18aef
feat: LoginMember create 메서드에 추가
chaehyunL Jun 29, 2026
66728ba
feat: String name 이용
chaehyunL Jun 29, 2026
74f36bd
feat: loginMember 생성
chaehyunL Jun 29, 2026
f0ce4c0
style: ctrl + alt+ l 개행정리
chaehyunL Jun 29, 2026
44ad820
feat: WebConfig 생성
chaehyunL Jun 29, 2026
c5ae254
docs:README 파일 생성
chaehyunL Jun 29, 2026
05a06d3
feat: createToken 메서드 생성
chaehyunL Jun 30, 2026
7e0c71b
feat: token에 관한 책임 분리
chaehyunL Jun 30, 2026
95f915d
feat: resolveArgument 메서드 책임 분리
chaehyunL Jun 30, 2026
165b552
refector: checklogin 메서드 변경
chaehyunL Jun 30, 2026
40aff17
refector: http 관련 기능을 controller 로 이동
chaehyunL Jun 30, 2026
578d9a3
style: 개행정리
chaehyunL Jun 30, 2026
af5ff58
refector: 메서드 입력 객체 loginMember로 변경
chaehyunL Jul 1, 2026
b0692c5
test: 3단계 테스트 생성
chaehyunL Jul 3, 2026
d87a302
feat:resorce.properties 생성
chaehyunL Jul 4, 2026
4836e92
refector: secretKey 숨김
chaehyunL Jul 4, 2026
d808ed6
refector: extractToken 제거
chaehyunL Jul 4, 2026
4c1ffe7
feat:Login 핸들러 인터셉터 등록
chaehyunL Jul 4, 2026
f77ce23
feat:Login 핸들러 인터셉터 등록
chaehyunL Jul 4, 2026
0de02ba
refector: preHandle 내용 수정
chaehyunL Jul 4, 2026
2e26756
refector: 뭔래 있던 properties 중복되어서 삭제함
chaehyunL Jul 4, 2026
4730128
refector: jwt.secret 주석 처리 해제
chaehyunL Jul 4, 2026
0eae6c3
refector: jwt.secret->roomescape.auth.jwt.secret
chaehyunL Jul 4, 2026
653e960
refector:admin/** path 추가
chaehyunL Jul 4, 2026
8bb5385
style: 개행 정리
chaehyunL Jul 4, 2026
488f861
refector: Component 등록
chaehyunL Jul 4, 2026
aa2b5fa
feat: expiretime 구현
chaehyunL Jul 4, 2026
8895666
feat: TokenNotFoundException 생성
chaehyunL Jul 5, 2026
4184b79
refector: Exception 패키지 내로 이동
chaehyunL Jul 5, 2026
3bd4251
feat: Token 검증 로직 추가
chaehyunL Jul 5, 2026
ec8004e
feat: token 추출 방식 변경
chaehyunL Jul 6, 2026
8957601
feat: accessToken과 refreshToken으로 나눔
chaehyunL Jul 6, 2026
20a948c
feat: accessToken과 refreshToken으로 나눔
chaehyunL Jul 6, 2026
f948434
feat: accessToken이 정보를 받아오도록 변경
chaehyunL Jul 6, 2026
93465ad
feat: findById 메서드 구현
chaehyunL Jul 6, 2026
2fba555
feat: token->accessToken/refreshToken으로 구분
chaehyunL Jul 6, 2026
49b6d7f
docs:ReadMe 파일 작성
chaehyunL Jul 6, 2026
13b8925
refector: token 관련 수정
chaehyunL Jul 10, 2026
715960c
feat: cookieManager 생성
chaehyunL Jul 10, 2026
a272863
refector: cookieManager 이용해서 수정
chaehyunL Jul 10, 2026
cee6ff6
refector: 오류검증 로직 추가
chaehyunL Jul 10, 2026
9fda054
Merge branch 'chaehyunl' into mvc3
chaehyunL Jul 10, 2026
767742f
refector: gradle 의존성 대체
chaehyunL Jul 8, 2026
a3eebbe
feat:JPA 관련 설정 추가
chaehyunL Jul 8, 2026
262a651
feat: Entity 생성
chaehyunL Jul 8, 2026
c5cc7a3
feat: Entity 생성
chaehyunL Jul 8, 2026
6fff1f3
refector:JPA 방식으로 변경
chaehyunL Jul 8, 2026
4518389
refector: JPA 변경에 따른 수정
chaehyunL Jul 8, 2026
b9cc04b
refector: JPA 변경에 따른 수정
chaehyunL Jul 8, 2026
31d685e
refector: JPA 변경에 따른 수정
chaehyunL Jul 8, 2026
9c9ae75
refector: value->time
chaehyunL Jul 8, 2026
e3f65ef
refector: value->time
chaehyunL Jul 8, 2026
35ebc38
test: Jpa test와 기존 테스트 분리
chaehyunL Jul 8, 2026
0f3d928
findByEmail 메서드 추가
chaehyunL Jul 8, 2026
77abb17
refector:data.spl로 변경
chaehyunL Jul 8, 2026
cae9a52
refector:value->time_value
chaehyunL Jul 8, 2026
dd40b5a
refector: schema 삭제
chaehyunL Jul 8, 2026
07c4f55
refector: protected 추가
chaehyunL Jul 8, 2026
1ff8645
refector: time_value 수정
chaehyunL Jul 8, 2026
1373c71
refector: entityManager
chaehyunL Jul 15, 2026
3db99b7
style:ctrl_alt_l
chaehyunL Jul 15, 2026
80797fa
style:ctrl_alt_l
chaehyunL Jul 15, 2026
cd1a819
feat: entity매니저로 변경
chaehyunL Jul 15, 2026
74c0595
Merge branch 'chaehyunl' into JPA2
chaehyunL Jul 15, 2026
313e5ee
feat: entity매니저로 변경
chaehyunL Jul 15, 2026
fd3e903
docs:README 파일 생성
chaehyunL Jul 15, 2026
12e9014
feat: 필요 메서드 생성
chaehyunL Jul 15, 2026
7bb3fe5
feat: 필요 메서드 생성
chaehyunL Jul 15, 2026
72b8311
feat: 필요 메서드 생성
chaehyunL Jul 15, 2026
63b2eb1
refector:네이밍 변경
chaehyunL Jul 17, 2026
13f3d39
refector:네이밍 변경
chaehyunL Jul 17, 2026
5405303
refector:time_value->timeValue 이름 변경
chaehyunL Jul 17, 2026
6c6a2e9
refector:cookieManager 사용
chaehyunL Jul 17, 2026
e52407f
refector:JPA 식으로 변경
chaehyunL Jul 18, 2026
cb1863e
refector:Reservation 수정
chaehyunL Jul 18, 2026
2fb95bf
refector:JPA 수정
chaehyunL Jul 19, 2026
afcde38
refector: entity 삭제
chaehyunL Jul 19, 2026
a6013d1
refector: member 구조 변경
chaehyunL Jul 19, 2026
7be91b0
refector: 5단계 spl 변경
chaehyunL Jul 19, 2026
235214c
style: 개행정리
chaehyunL Jul 19, 2026
8fca327
test:6단계 추가
chaehyunL Jul 20, 2026
a610636
feat:6단계 관련 추가
chaehyunL Jul 20, 2026
f188d27
feat:6단계 관련 추가
chaehyunL Jul 20, 2026
aabe511
docs: README 수정
chaehyunL Jul 20, 2026
b930ef6
refector:waiting 추가
chaehyunL Jul 20, 2026
10eb09f
feat:waiting 관련 java 추가
chaehyunL Jul 20, 2026
129ab0d
feat:waiting 관련 java 추가
chaehyunL Jul 20, 2026
bcc5974
refecter: 메서드 형식 한개로 바꿈
chaehyunL Jul 21, 2026
9e41a00
feat:ThemeService 생성
chaehyunL Jul 21, 2026
315b5ae
feat:ThemeService 생성
chaehyunL Jul 21, 2026
1c74bc1
refector: Reservation dto 객체 가지지 않도록 수정
chaehyunL Jul 24, 2026
0b719fd
refector: 개행정리
chaehyunL Jul 24, 2026
4d167b5
feat: unique 제약 조건 추가
chaehyunL Jul 25, 2026
6c2cee4
feat: 어노테이션 추가
chaehyunL Jul 25, 2026
e152c74
feat: @Getter 어노테이션 추가
chaehyunL Jul 25, 2026
7477b48
feat: WaitingRepository 생성
chaehyunL Jul 25, 2026
d6e9530
refector: dto 형태 record로 변경
chaehyunL Jul 25, 2026
1cc41fb
refector: dto class->record 수정에 따른 변경
chaehyunL Jul 25, 2026
418bb7c
feat:Builder 생성
chaehyunL Jul 25, 2026
415b116
refector: Builder 사용
chaehyunL Jul 25, 2026
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 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0'

Expand Down
24 changes: 11 additions & 13 deletions src/main/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,26 @@

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

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


---
## Spring MVC 미션 3단계 - 관리자 기능
## Spring JPA 4-6단계 Entity Manager-> JPA

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

어드민 페이지 진입은 admin 권한이 있는 사람만 할 수 있도록 제한한다.
만약 진입 권한이 없다면 401 코드를 응답하도록 한다.

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

- HandlerInterceptor에서 기본적으로 preHandle/postHandle/afterCompletion으로 나뉘어져있던데,
이번의 경우에는 admin페이지 접속만을 확인하면 되는거라서 preHandle만을 사용했습니다.
이경우에는 postHandle과 afterCompletion을 어떻게 구성하면 좋을지..? 알고 싶네요.


- WebConfig 같은 경우에는, admin 페이지를 접속할때의 role만을 확인해주면 되므로 /admin/***을 사용해주었습니다.
사용해보니, JPA가 EntityManager보다 더 적은 메서드를 쓰고도 충분히 구현되는 것 같습니다.!

- token 만료시간도 있어야할 것 같아서 expire_time을 이용해서,만료시간을 구현해보았습니다. 만료시간을 구현할 더 좋은
방법이 있을지?? 궁금합니다.
실무에서는 JPA를 더 많이 사용하게 될 것 같은데, EntityManager를 사용하게 되는 경우도 있나요?

- token을 accessToken과 refreshToken으로 나눠서 구현해주었는데, 구현하다보니 너무 어려워서 제대로 구현된건지 잘
모르겠습니다.

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


4 changes: 2 additions & 2 deletions src/main/java/roomescape/CookieManager.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package roomescape;

import jakarta.servlet.http.Cookie;
import org.springframework.stereotype.Component;
import roomescape.exception.TokenNotFoundException;

import java.util.Arrays;

@Component
public class CookieManager {


public String extractToken(Cookie[] cookies, String cookieName) {
return Arrays.stream(cookies)
.filter(cookie -> cookieName.equals(cookie.getName()))
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/roomescape/login/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public ResponseEntity<Void> refresh(HttpServletRequest request, HttpServletRespo

@GetMapping("/login/check")
public ResponseEntity<MemberResponse> checkLogin(LoginMember loginMember) {
Long memberId = loginMember.id();
Long memberId = loginMember.getId();
MemberResponse memberResponse = loginService.checkLogin(memberId);
return ResponseEntity.ok(memberResponse);
}
Expand Down
25 changes: 17 additions & 8 deletions src/main/java/roomescape/login/LoginInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,41 @@
import roomescape.CookieManager;
import roomescape.JwtProvider;
import roomescape.member.Member;
import roomescape.member.MemberDao;
import roomescape.member.MemberRepository;

@Component
public class LoginInterceptor implements HandlerInterceptor {

Comment on lines +11 to 15

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 관리자임을 확인하고 권한 부여한다고 생각해서

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

private final JwtProvider jwtProvider;
private final MemberDao memberDao;
private final MemberRepository memberRepository;
private final CookieManager cookieManager;

public LoginInterceptor(JwtProvider jwtProvider, MemberDao memberDao, CookieManager cookieManager) {
public LoginInterceptor(JwtProvider jwtProvider, MemberRepository memberRepository, CookieManager cookieManager) {
this.jwtProvider = jwtProvider;
this.memberDao = memberDao;
this.memberRepository = memberRepository;
this.cookieManager = cookieManager;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Cookie[] cookies = request.getCookies();
Cookie[] cookies = getCookies(request);

String token = cookieManager.extractToken(cookies, "accessToken");
String token = cookieManager.extractToken(cookies,"accessToken");
Long memberId = jwtProvider.getMemberId(token);
Member member = memberDao.findById(memberId);
if (member == null || !member.getRole().equals("ADMIN")) {
Member member = memberRepository.findById(memberId)
.orElseThrow();
if (!member.getRole().equals("ADMIN")) {
response.setStatus(401);
return false;
}
return true;
}

private Cookie[] getCookies(HttpServletRequest httpServletRequest) {
Cookie[] cookies = httpServletRequest.getCookies();
if (cookies == null) {
throw new IllegalStateException("cookie is not exist");
}
return cookies;
}
}
51 changes: 45 additions & 6 deletions src/main/java/roomescape/login/LoginMember.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
package roomescape.login;

public record LoginMember(
Long id,
String name,
String email,
String role
) {
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;


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를 빼는게 나을 것 가타요


public LoginMember(Long id, String name, String email, String password, String role) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.role = role;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getEmail() {
return email;
}

public String getPassword() {
return password;
}

public String getRole() {
return role;
}
}
14 changes: 7 additions & 7 deletions src/main/java/roomescape/login/LoginMemberArgumentResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
import roomescape.CookieManager;
import roomescape.JwtProvider;
import roomescape.member.Member;
import roomescape.member.MemberDao;
import roomescape.member.MemberRepository;

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

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 권한을 가지고 있는 사람에게 권한을 부여해줍니다

private final MemberRepository memberRepository;
private final JwtProvider jwtProvider;
private final CookieManager cookieManager;
public LoginMemberArgumentResolver(JwtProvider jwtProvider, MemberDao memberDao,CookieManager cookieManager) {
this.memberDao = memberDao;
public LoginMemberArgumentResolver(JwtProvider jwtProvider, MemberRepository memberRepository,CookieManager cookieManager) {
this.memberRepository = memberRepository;
this.jwtProvider = jwtProvider;
this.cookieManager=cookieManager;
}
Expand All @@ -38,9 +38,10 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m

Long memberId = jwtProvider.getMemberId(token);

Member member = memberDao.findById(memberId);
Member member = memberRepository.findById(memberId)
.orElseThrow();

return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
return new LoginMember(memberId, member.getName(), member.getEmail(), member.getPassword(),member.getRole());
}

private HttpServletRequest getRequest(NativeWebRequest nativeWebRequest) {
Expand All @@ -58,5 +59,4 @@ private Cookie[] getCookies(HttpServletRequest httpServletRequest) {
}
return cookies;
}

}
24 changes: 11 additions & 13 deletions src/main/java/roomescape/login/LoginService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,34 @@

import org.springframework.stereotype.Service;
import roomescape.member.Member;
import roomescape.member.MemberDao;
import roomescape.member.MemberRepository;
import roomescape.member.MemberResponse;

@Service
public class LoginService {
private final MemberDao memberDao;
private final MemberRepository memberRepository;

public LoginService(MemberDao memberDao) {
this.memberDao = memberDao;
public LoginService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

public Member login(String email, String password) {
Member member = memberDao.findByEmailAndPassword(email, password);
if (member == null) {
throw new RuntimeException("Invalid email or password");
}
Member member = memberRepository.findByEmailAndPassword(email, password)
.orElseThrow();

return member;
}

public MemberResponse checkLogin(Long memberId) {

Member member = memberDao.findById(memberId);
if (member == null) {
throw new RuntimeException("Invalid email or password");
}
Member member = memberRepository.findById(memberId)
.orElseThrow();
return new MemberResponse(member.getId(), member.getName(), member.getEmail());
}

public Member findById(Long id) {
Member member = memberDao.findById(id);
Member member = memberRepository.findById(id)
.orElseThrow();

return member;
}
Expand Down
20 changes: 11 additions & 9 deletions src/main/java/roomescape/member/Member.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
package roomescape.member;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;
private String password;
private String role;
Comment on lines +17 to 27

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 제약이 걸려있다고 하셨는데, 어떤 패키지를 통해 확인할 수 있나요?


public Member(Long id, String name, String email, String role) {
this.id = id;
this.name = name;
this.email = email;
this.role = role;
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.

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


public Member(String name, String email, String password, String role) {
this.name = name;
this.email = email;
this.password = password;
this.role = role;
}

public Long getId() {
return id;
}
public Long getId() { return id; }

public String getName() {
return name;
Expand Down
68 changes: 0 additions & 68 deletions src/main/java/roomescape/member/MemberDao.java

This file was deleted.

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

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

import java.util.Optional;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {

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

Optional<Member> findByName(String name);
}
Loading