Skip to content
Open
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3d3151e
refactor: Id로 맴버 조회 dao 추가
mintcoke123 Dec 26, 2025
0c3e685
feat: POST/login 추가
mintcoke123 Dec 27, 2025
8b26e96
feat: 토큰 조회
mintcoke123 Dec 27, 2025
378e7c5
refactor: 키 제사용 가능하게 변경
mintcoke123 Dec 27, 2025
cfd15f6
test: 이단계 테스트 추가
mintcoke123 Dec 27, 2025
b456b89
로그인맴버 dto 추가
mintcoke123 Dec 27, 2025
97cf233
feat: resolver,config 추가
mintcoke123 Dec 27, 2025
451beff
refactor: reservationRequest에 생성자 생성
mintcoke123 Dec 27, 2025
f1e3288
refactor: createToken 메서드분리
mintcoke123 Dec 27, 2025
0d5c991
test: 테스트코드 리팩토링
mintcoke123 Dec 27, 2025
f6a7db0
refactor: 테스트코드대로 컨트롤러 수정
mintcoke123 Dec 27, 2025
20d7526
test: 삼단계 테스트코드 작성
mintcoke123 Dec 27, 2025
1357d07
feat: interceptor 구현
mintcoke123 Dec 27, 2025
12171bb
refactor: auth의 중복 제거
mintcoke123 Dec 31, 2025
1eb6513
refactor: 프로퍼티 주입으로 시크릿 키 관리
mintcoke123 Dec 31, 2025
a108bc6
refactor: application.properties 수정
mintcoke123 Dec 31, 2025
069e91f
test: 테스트코드 리팩토링
mintcoke123 Dec 31, 2025
f7a7485
refactor: 생성자 주입으로 테스트코드 변경
mintcoke123 Dec 31, 2025
b4869c1
test: truncate 데이터 구축
mintcoke123 Dec 31, 2025
97e0106
refactor: 검증을 requestDto단에서 적용하도록 변경
mintcoke123 Dec 31, 2025
4b90a36
refactor: 어드민 페이지 엔드포인트 수정
mintcoke123 Dec 31, 2025
9636a93
test: 미션 테스트코드 변경
mintcoke123 Jan 1, 2026
06b14d0
test: 미션 테스트코드 변경
mintcoke123 Jan 1, 2026
12f68bb
refactor: TimeDao에서 id로 time을 확인할 수 있게끔 변경
mintcoke123 Jan 1, 2026
ee61649
refactor: gradle 파일 수정
mintcoke123 Jan 1, 2026
589badc
test: 테스트코드 추가, dao를 jpa 방식으로 변경하며 삭제
mintcoke123 Jan 1, 2026
d70f3c3
refactor: dao를 repository 방식으로 변경경
mintcoke123 Jan 1, 2026
ee530e2
refactor: JdbcTemplate 기반 Dao를 jpa 레포지토리로 리팩토링
mintcoke123 Jan 1, 2026
7554215
test: 테스트코드를 바뀐 코드에 걸맞게 변경
mintcoke123 Jan 1, 2026
8d6b22b
refactor: 스키마, 시드 정리
mintcoke123 Jan 1, 2026
3a20e9f
refactor: 멤버 엔티티 매핑
mintcoke123 Jan 1, 2026
aacf60c
refactor: 내 예약 responseDto 구현
mintcoke123 Jan 1, 2026
b935aca
refactor: LoginMember 주입받아 서비스 호출출
mintcoke123 Jan 1, 2026
43de312
refactor: ReservationRepository 리팩토링
mintcoke123 Jan 1, 2026
744396a
test: 테스트코드 변경
mintcoke123 Jan 1, 2026
be881da
refactor: 스키마 테이블, 테스트코드 테이블블 변경
mintcoke123 Jan 1, 2026
ce83b57
feat: waiting 엔티티 추가
mintcoke123 Jan 1, 2026
c61db0a
feat: WaitingService 및 WaitingController추가가
mintcoke123 Jan 1, 2026
d7e38ec
refactor: step-6 구현
mintcoke123 Jan 1, 2026
1c30a05
refactor: waitingResponse의 setter 추가(테스트코드용)
mintcoke123 Jan 1, 2026
a44bd1f
refactor: myReservationResponse에 jsonIgnore 적용
mintcoke123 Jan 1, 2026
739e9db
refactor: 사용하지 않는 메서드 삭제
mintcoke123 Jan 7, 2026
c05f17e
refactor: @getter 어노테이션 적용
mintcoke123 Jan 7, 2026
5b99e63
refactor: 미사용중인 import 문 제거
mintcoke123 Jan 7, 2026
68ae9f5
refactor: 에러코드 수정
mintcoke123 Jan 7, 2026
c8ef4c0
refactor: dto로 네이밍 변경
mintcoke123 Jan 7, 2026
26a581a
refactor: record 사용으로 수정
mintcoke123 Jan 8, 2026
0149b40
refactor: service 의존성 필드에 final 사용
mintcoke123 Jan 8, 2026
94fd21f
refactor: @transactional 적용
mintcoke123 Jan 8, 2026
134fb43
refactor: ApiError 확장 후 적용
mintcoke123 Jan 8, 2026
43a92c7
refactor: Jwt 만료시간 관리 일관화
mintcoke123 Jan 8, 2026
6358623
refactor: N+1 방지, 상수에 enum도입
mintcoke123 Jan 8, 2026
02dc95d
refactor: 리뷰 반영
mintcoke123 Jan 16, 2026
b69d20f
refactor: 리뷰 반영(2차)
mintcoke123 Jan 18, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/


application-local.properties
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ 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:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'

implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0'

Expand Down
49 changes: 42 additions & 7 deletions src/main/java/roomescape/ExceptionController.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,49 @@
package roomescape;

import jakarta.persistence.EntityNotFoundException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import roomescape.common.ApiError;

@ControllerAdvice
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;

@RestControllerAdvice
public class ExceptionController {

@70825 70825 Jan 8, 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.

여기도 따로 패키지로 관리하면 좋아보이네요. 따로 패키지를 두지 않은 이유가 있나요?

  • 이외에도 WebConfig, PageController 도 마찬가지입니다

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.

리팩토링을 진행할 때 신경쓴 범위가 너무 좁았던 것 같습니다!
다만 지적해주신 것처럼 공용 관심사 성격이 명확한 클래스들이기 때문에, ExceptionController를roomescape.common.ExceptionController로 이동하였습니다!

@ExceptionHandler(Exception.class)
public ResponseEntity<Void> handleRuntimeException(Exception e) {
e.printStackTrace();
return ResponseEntity.badRequest().build();
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidation(MethodArgumentNotValidException e) {
return build(ApiError.BAD_REQUEST_INVALID_INPUT);
}

@ExceptionHandler({IllegalStateException.class})
public ResponseEntity<Map<String, Object>> handleIllegalState(IllegalStateException e) {
return build(ApiError.BAD_REQUEST_ILLEGAL_STATE);
}

@ExceptionHandler({NoSuchElementException.class, EntityNotFoundException.class})
public ResponseEntity<Map<String, Object>> handleNotFound(RuntimeException e) {
return build(ApiError.NOT_FOUND_RESOURCE);
}

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<Map<String, Object>> handleNotReadable(HttpMessageNotReadableException e) {
return build(ApiError.BAD_REQUEST_INVALID_INPUT);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleUnknown(Exception e) {
e.printStackTrace();
return build(ApiError.INTERNAL_SERVER_ERROR);
}

private ResponseEntity<Map<String, Object>> build(ApiError apiError) {
Map<String, Object> body = new HashMap<>();
body.put("code", apiError.getCode());
body.put("message", apiError.getMessage());
return ResponseEntity.status(apiError.getHttpStatus()).body(body);
}
}
30 changes: 30 additions & 0 deletions src/main/java/roomescape/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package roomescape;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import roomescape.auth.AdminAuthInterceptor;
import roomescape.auth.LoginMemberArgumentResolver;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${roomescape.auth.jwt.secret}")
private String secretKey;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver(secretKey));
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AdminAuthInterceptor(secretKey))
.addPathPatterns("/admin", "/admin/**");
}
}


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

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import roomescape.common.ApiError;
import roomescape.util.JwtUtil;

import java.io.IOException;

public class AdminAuthInterceptor implements HandlerInterceptor {

private final String secretKey;

public AdminAuthInterceptor(String secretKey) {
this.secretKey = secretKey;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = JwtUtil.extractTokenFromCookies(request.getCookies());
if (token.isEmpty()) {
writeError(response, ApiError.UNAUTHORIZED_MISSING_TOKEN);
return false;
}

try {
Claims claims = JwtUtil.parseClaims(token, secretKey);
String role = claims.get("role", String.class);
if (!"ADMIN".equals(role)) {
writeError(response, ApiError.FORBIDDEN_ADMIN_ONLY);
return false;
}
return true;
} catch (ExpiredJwtException e) {
writeError(response, ApiError.UNAUTHORIZED_EXPIRED_TOKEN);
return false;
} catch (Exception e) {
writeError(response, ApiError.UNAUTHORIZED_INVALID_TOKEN);
return false;
}
}

private void writeError(HttpServletResponse response, ApiError apiError) {
response.setStatus(apiError.getHttpStatus().value());
response.setContentType("application/json;charset=UTF-8");
try {
String payload = "{\"code\":" + apiError.getCode() + ",\"message\":\"" + apiError.getMessage() + "\"}";
response.getWriter().write(payload);
} catch (IOException ignored) {
}
}
}
Comment on lines +46 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

나중에 이런 곳에 에러 로그 (ex. slf4j)를 추가하는 것도 좋아보이네요~

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.

넵! 이후 코드를 짤때는 logger를 적용해보겠습니다!



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

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import roomescape.member.LoginMemberDto;
import roomescape.util.JwtUtil;

public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

private final String secretKey;

public LoginMemberArgumentResolver(String secretKey) {
this.secretKey = secretKey;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(LoginMemberDto.class);
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
String token = JwtUtil.extractTokenFromCookies(request.getCookies());

if (token.isEmpty()) {
return null;
}

try {
Claims claims = JwtUtil.parseClaims(token, secretKey);

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

return new LoginMemberDto(id, name, null, role);
} catch (ExpiredJwtException e) {
return null;
} catch (Exception e) {
return null;
}
}
}
29 changes: 29 additions & 0 deletions src/main/java/roomescape/common/ApiError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package roomescape.common;

import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
public enum ApiError {
BAD_REQUEST_INVALID_INPUT(HttpStatus.BAD_REQUEST, 40001, "잘못된 요청입니다."),
BAD_REQUEST_ILLEGAL_STATE(HttpStatus.BAD_REQUEST, 40002, "요청을 처리할 수 없습니다."),
NOT_FOUND_RESOURCE(HttpStatus.NOT_FOUND, 40401, "리소스를 찾을 수 없습니다."),
UNAUTHORIZED_MISSING_TOKEN(HttpStatus.UNAUTHORIZED, 40101, "토큰이 없습니다."),
UNAUTHORIZED_INVALID_TOKEN(HttpStatus.UNAUTHORIZED, 40102, "토큰이 유효하지 않습니다."),
UNAUTHORIZED_EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, 40103, "토큰이 만료되었습니다."),
FORBIDDEN_ADMIN_ONLY(HttpStatus.FORBIDDEN, 40301, "관리자 권한이 필요합니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 50000, "서버 오류가 발생했습니다.");

@70825 70825 Jan 8, 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.

요거 보면서 궁금한 점이 있는데요. 이걸 사용하면 어떤 점이 장점으로 느껴지시나요?? 특히 아래의 장점이 무엇일지 궁금해요

  1. 프론트엔드에서 응답값으로 아래 message를 받았을 때의 장점
  2. code 값을 사용해서 얻는 장점

다른 클래스들도 맨 아래에 개행이 여러줄로 되어있던데 한줄로 삭제부탁드려요~
reformat code를 사용하면 정리될 것으로 보이네요

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.

  1. 사용자에게 바로 보여줄 수 있는 문구를 서버가 일관되게 내려줄 수 있습니다. 또 문구가 바뀌더라도 서버가 책임지고 수정할 수 있어서 프론트가 화면 문구를 따로 하드코딩하지 않아도 됩니다.

  2. 문구는 변경되기 쉬운데, code는 상대적으로 고정이라 프론트가 code로 안정적으로 분기할 수 있습니다! 예를 들어 401 code가 떴을 때 토큰 없음/토큰 만료/토큰 무효에 따라 각기 다를 처리를 요청할 수 있습니다!


private final HttpStatus httpStatus;
private final int code;
private final String message;

ApiError(HttpStatus httpStatus, int code, String message) {
this.httpStatus = httpStatus;
this.code = code;
this.message = message;
}
}



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

public record LoginMemberDto(Long id, String name, String email, String role) {}



18 changes: 14 additions & 4 deletions src/main/java/roomescape/member/Member.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
package roomescape.member;

import jakarta.persistence.*;

@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Column(unique = true)
private String email;
Comment on lines 11 to 12

@70825 70825 Jan 8, 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.

요거 최근에 팀에서 이야기 나왔던 내용인데, 동현님의 생각도 궁금하네요

  1. 여기 필드에만 @Column(unique = true)만 설정한 이유가 있나요? or 다른 필드에@Column(unique = false)를 설정할 수도 있는데 설정하지 않은 이유가 있을까요?
  2. 만약 전자를 택하는 사람과 후자를 택하는 사람이 같이 개발을 한다면 한쪽에 맞춰주는걸 규칙으로 정하는게 나을까요?

팀에서는 이런 이야기가 나왔었어요

  • 어차피 기본값이 @Column(unique = false)인데, 굳이 적어둘 필요가 없어보임
  • 기본값 자료형이 boolean이라 true or false 라고는 해도, 결국엔 내가 이런 옵션이 있다는걸 알아야하기 때문에 적어두는게 좋음
  • 개발할 때 중요한 사항은 아니므로 각자 스타일대로 코딩해도 문제 없음
  • 코드에도 통일성이 중요하기 때문에 만약 따로따로 스타일을 가져가데 된다면 신규입사자들이 우리팀 코드를 보면 혼란스러워할 수도 있음

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.

  1. 이메일을 회원의 유일한 식별값으로 보고, 중복이 일어나지 않아야 하기 때문에 unique = true를 적용하였습니다! Column(unique = true) 는 제약사항이기 때문에, 제약이 걸리지 않는 부분보다는 제한이 걸리는 부분만 표기하는 것이 맞다고 판단했습니다!

  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.

이메일을 회원의 유일한 식별값으로 보고, 중복이 일어나지 않아야 하기 때문에 unique = true를 적용하였습니다! Column(unique = true) 는 제약사항이기 때문에, 제약이 걸리지 않는 부분보다는 제한이 걸리는 부분만 표기하는 것이 맞다고 판단했습니다.
저는 한쪽으로 맞추는 규칙을 정하는 게 낫다고 생각합니다!! 기능적으로는 상관없지만, 스타일이 섞이면 같은 의미의 코드가 파일마다 다르게 보이는 문제가 발생할 것이라 생각합니다!

모두 저랑 같은 생각이네요 ㅋㅋ 저도 unique = true만 명시적으로 해두면 나머지는 false인 것을 알게되니까 무의미한 글자가 될 수 있는걸 방지해주기도 하고, 한쪽으로 규칙 맞추는게 좀 더 통일성 있다고 생각해요

private String password;
private String role;
@Enumerated(EnumType.STRING)
private Role role;

public Member() {
}

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

public Member(String name, String email, String password, String role) {
public Member(String name, String email, String password, Role role) {
this.name = name;
this.email = email;
this.password = password;
Expand All @@ -37,7 +47,7 @@ public String getPassword() {
return password;
}

public String getRole() {
public Role getRole() {
return role;
}
}
48 changes: 41 additions & 7 deletions src/main/java/roomescape/member/MemberController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,68 @@
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import roomescape.util.JwtUtil;

import java.net.URI;

@RestController
public class MemberController {
private MemberService memberService;
@Value("${roomescape.auth.jwt.secret}")
private String secretKey;

public MemberController(MemberService memberService) {
this.memberService = memberService;
}

@PostMapping("/members")
public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) {
MemberResponse member = memberService.createMember(memberRequest);
return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member);
public ResponseEntity createMember(@RequestBody MemberRequestDto memberRequest) {
MemberResponseDto member = memberService.createMember(memberRequest);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

요거는 MemberResponse 대신 MemberResponseDto로 변경한 이유가 있나요?!

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.

Dto 가 명시되어야 하기 때문입니다! 제가 처음부터 구조를 짰다면 dto 폴더를 별도로 만들었을 텐데, 기존의 코드에 덧대어 코드를 작성하면서 dto의 이름을 MemberResponse고 하고 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 폴더를 별도로 만든다는게 member, reservation, ... 과 같은 레벨의 패키지를 둔다는 것인가요??

/src/main/java
ㄴ auth
ㄴ member
ㄴ reservation
ㄴ ...
ㄴ dto

요러면 그냥 member 패키지 안에 dto 패키지를 생성해도 문제 없을 것으로 보여서요

/src/main/java
ㄴ auth
    ㄴ dto
ㄴ member
    ㄴ dto
ㄴ reservation
    ㄴ dto
ㄴ ...

return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member);
}

@PostMapping("/login")
public ResponseEntity login(@RequestBody MemberRequestDto memberRequest, HttpServletResponse response) {
Member member = memberService.login(memberRequest.email(), memberRequest.password());

String accessToken = createToken(member);

Cookie cookie = JwtUtil.createAuthCookie(accessToken, JwtUtil.DEFAULT_MAX_AGE_SECONDS, false);
response.addCookie(cookie);

return ResponseEntity.ok().build();
}

@GetMapping("/login/check")
public ResponseEntity<MemberResponseDto> checkLogin(HttpServletRequest request) {
String token = JwtUtil.extractTokenFromCookies(request.getCookies());

String name = JwtUtil.parseClaims(token, secretKey).get("name", String.class);

MemberResponseDto body = new MemberResponseDto(null, name, null);
return ResponseEntity.ok(body);
}

public String createToken(Member member) {
return JwtUtil.createToken(member.getId().toString(), member.getName(), member.getRole(), secretKey);
}


public String createTokenFromEmailAndPassword(String email, String password) {
Member member = memberService.login(email, password);
return JwtUtil.createToken(member.getId().toString(), member.getName(), member.getRole(), secretKey);
}


@PostMapping("/logout")
public ResponseEntity logout(HttpServletResponse response) {
Cookie cookie = new Cookie("token", "");
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(0);
Cookie cookie = JwtUtil.createExpiredAuthCookie();
response.addCookie(cookie);
return ResponseEntity.ok().build();
}
Expand Down
Loading