From 0289237f63615066a8fa11e74e05b4717577727b Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:25:36 +0900 Subject: [PATCH 01/16] =?UTF-8?q?setting=20:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build.gradle b/build.gradle index 5c5beb9..2c7bbae 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,14 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + //security + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'org.springframework.boot:spring-boot-starter-validation' + + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' @@ -38,6 +46,10 @@ dependencies { // test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + //lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { From e4e2745d2b7bb4f6c2014fb978a0912b0926d218 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:26:14 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat=20:=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ita/growin/domain/user/constant/Work.java | 5 +++ .../ita/growin/domain/user/entity/User.java | 44 ++++++++++++++----- .../ita/growin/global/entity/BaseEntity.java | 24 ++++++++++ 3 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ita/growin/domain/user/constant/Work.java create mode 100644 src/main/java/ita/growin/global/entity/BaseEntity.java diff --git a/src/main/java/ita/growin/domain/user/constant/Work.java b/src/main/java/ita/growin/domain/user/constant/Work.java new file mode 100644 index 0000000..87afc4f --- /dev/null +++ b/src/main/java/ita/growin/domain/user/constant/Work.java @@ -0,0 +1,5 @@ +package ita.growin.domain.user.constant; + +public enum Work { + MIDDLE_TO_HIGH_STUDENT, GRAGUATE_STUDENT, OFFICE_WORKER, OTHER +} diff --git a/src/main/java/ita/growin/domain/user/entity/User.java b/src/main/java/ita/growin/domain/user/entity/User.java index eece230..28604d9 100644 --- a/src/main/java/ita/growin/domain/user/entity/User.java +++ b/src/main/java/ita/growin/domain/user/entity/User.java @@ -1,26 +1,50 @@ package ita.growin.domain.user.entity; - import ita.growin.domain.event.entity.Event; +import ita.growin.domain.user.constant.*; +import ita.growin.global.entity.BaseEntity; import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; +import org.hibernate.annotations.Comment; import java.util.ArrayList; import java.util.List; @Entity +@Table(name = "users") @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Builder @AllArgsConstructor -public class User { +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class User extends BaseEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "user_id") - private Long id; + @GeneratedValue + @Comment("uid") + private Long userId; + + @Column(nullable = false, length = 50) + private String email; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private LoginType type; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private UserStatus status; + + @Column(nullable = false, length = 100) + private String nickname; + + @Enumerated(EnumType.STRING) + private Work work; + + @Enumerated(EnumType.STRING) + private InterestField interestField; + + @Enumerated(EnumType.STRING) + private Target target; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List events = new ArrayList<>(); diff --git a/src/main/java/ita/growin/global/entity/BaseEntity.java b/src/main/java/ita/growin/global/entity/BaseEntity.java new file mode 100644 index 0000000..f701fa1 --- /dev/null +++ b/src/main/java/ita/growin/global/entity/BaseEntity.java @@ -0,0 +1,24 @@ +package ita.growin.global.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseEntity { + + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; +} From 6649c2eeec60e39c5bce2b4283b86d58b551be3f Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:26:51 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat=20:=20dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/dto/request/KakaoLoginRequest.java | 15 +++++++ .../auth/dto/request/KakaoSignupRequest.java | 27 +++++++++++++ .../auth/dto/request/RefreshTokenRequest.java | 10 +++++ .../auth/dto/response/AuthResponse.java | 15 +++++++ .../domain/auth/dto/response/UserDto.java | 40 +++++++++++++++++++ 5 files changed, 107 insertions(+) create mode 100644 src/main/java/ita/growin/domain/auth/dto/request/KakaoLoginRequest.java create mode 100644 src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java create mode 100644 src/main/java/ita/growin/domain/auth/dto/request/RefreshTokenRequest.java create mode 100644 src/main/java/ita/growin/domain/auth/dto/response/AuthResponse.java create mode 100644 src/main/java/ita/growin/domain/auth/dto/response/UserDto.java diff --git a/src/main/java/ita/growin/domain/auth/dto/request/KakaoLoginRequest.java b/src/main/java/ita/growin/domain/auth/dto/request/KakaoLoginRequest.java new file mode 100644 index 0000000..1d4213b --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/request/KakaoLoginRequest.java @@ -0,0 +1,15 @@ +package ita.growin.domain.auth.dto.request; + +import jakarta.validation.constraints.NotBlank; + +import lombok.Getter; + + +@Getter +public class KakaoLoginRequest { + + @NotBlank(message = "Access Token은 필수입니다.") + private String accessToken; + + private String deviceToken; +} \ No newline at end of file diff --git a/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java b/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java new file mode 100644 index 0000000..d1f4c05 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java @@ -0,0 +1,27 @@ +package ita.growin.domain.auth.dto.request; + +import ita.growin.domain.user.constant.InterestField; +import ita.growin.domain.user.constant.Target; +import ita.growin.domain.user.constant.Work; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; + +import lombok.Getter; + +@Getter +public class KakaoSignupRequest { + + @NotBlank(message = "Access Token은 필수입니다.") + private String accessToken; + + @NotNull(message = "직업 정보는 필수입니다.") + private Work work; + + @NotNull(message = "관심분야는 필수입니다.") + private InterestField interestField; + + @NotNull(message = "목표는 필수입니다.") + private Target target; + + private String deviceToken; +} \ No newline at end of file diff --git a/src/main/java/ita/growin/domain/auth/dto/request/RefreshTokenRequest.java b/src/main/java/ita/growin/domain/auth/dto/request/RefreshTokenRequest.java new file mode 100644 index 0000000..7e13fbf --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/request/RefreshTokenRequest.java @@ -0,0 +1,10 @@ +package ita.growin.domain.auth.dto.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; + +@Getter +public class RefreshTokenRequest { + @NotBlank(message = "Refresh Token은 필수입니다.") + private String refreshToken; +} diff --git a/src/main/java/ita/growin/domain/auth/dto/response/AuthResponse.java b/src/main/java/ita/growin/domain/auth/dto/response/AuthResponse.java new file mode 100644 index 0000000..83875dd --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/response/AuthResponse.java @@ -0,0 +1,15 @@ +package ita.growin.domain.auth.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class AuthResponse { + private String accessToken; + private String refreshToken; + private String tokenType; + private Long expiresIn; + private UserDto user; +} + diff --git a/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java b/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java new file mode 100644 index 0000000..2c52c13 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java @@ -0,0 +1,40 @@ +package ita.growin.domain.auth.dto.response; + +import ita.growin.domain.user.constant.*; +import ita.growin.domain.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor +@Builder +public class UserDto { + private Long userId; + private String email; + private String nickname; + private LoginType type; + private UserStatus status; + private Work work; + private InterestField interestField; + private Target target; + private LocalDateTime createdAt; + private Boolean isNewUser; + + public static UserDto from(User user) { + return UserDto.builder() + .userId(user.getUserId()) + .email(user.getEmail()) + .nickname(user.getNickname()) + .type(user.getType()) + .status(user.getStatus()) + .work(user.getWork()) + .interestField(user.getInterestField()) + .target(user.getTarget()) + .createdAt(user.getCreatedAt()) + .isNewUser(false) + .build(); + } +} From 1f22df08ab9bda6ed1feb70944cfaaf21c0b4bee Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:27:11 +0900 Subject: [PATCH 04/16] feat : auth controller, service --- .../auth/controller/AuthController.java | 61 ++++++ .../domain/auth/service/AuthService.java | 176 ++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 src/main/java/ita/growin/domain/auth/controller/AuthController.java create mode 100644 src/main/java/ita/growin/domain/auth/service/AuthService.java diff --git a/src/main/java/ita/growin/domain/auth/controller/AuthController.java b/src/main/java/ita/growin/domain/auth/controller/AuthController.java new file mode 100644 index 0000000..520808f --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/controller/AuthController.java @@ -0,0 +1,61 @@ +package ita.growin.domain.auth.controller; + +import ita.growin.domain.auth.dto.request.KakaoLoginRequest; +import ita.growin.domain.auth.dto.request.KakaoSignupRequest; +import ita.growin.domain.auth.dto.request.RefreshTokenRequest; +import ita.growin.domain.auth.dto.response.AuthResponse; +import ita.growin.domain.auth.service.AuthService; +import ita.growin.global.response.APIResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequestMapping("/api/v1/auth") +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + + @PostMapping("/kakao/signup") + public ResponseEntity> kakaoSignup( + @Valid @RequestBody KakaoSignupRequest request + ) { + log.info("카카오 회원가입 요청"); + AuthResponse response = authService.kakaoSignup(request); + return ResponseEntity.status(HttpStatus.CREATED) + .body(APIResponse.success(response)); + } + + @PostMapping("/kakao/login") + public ResponseEntity> kakaoLogin( + @Valid @RequestBody KakaoLoginRequest request + ) { + log.info("카카오 로그인 요청"); + AuthResponse response = authService.kakaoLogin(request); + return ResponseEntity.ok(APIResponse.success(response)); + } + + @PostMapping("/refresh") + public ResponseEntity> refreshToken( + @Valid @RequestBody RefreshTokenRequest request + ) { + log.info("토큰 갱신 요청"); + AuthResponse response = authService.refreshToken(request); + return ResponseEntity.ok(APIResponse.success(response)); + } + + @PostMapping("/logout") + public ResponseEntity> logout( + @RequestAttribute("userId") Long userId + ) { + log.info("로그아웃 요청 - User ID: {}", userId); + authService.logout(userId); + return ResponseEntity.ok(APIResponse.success(null)); + } +} + diff --git a/src/main/java/ita/growin/domain/auth/service/AuthService.java b/src/main/java/ita/growin/domain/auth/service/AuthService.java new file mode 100644 index 0000000..f13b999 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/service/AuthService.java @@ -0,0 +1,176 @@ +package ita.growin.domain.auth.service; + +import ita.growin.domain.auth.dto.request.KakaoLoginRequest; +import ita.growin.domain.auth.dto.request.KakaoSignupRequest; +import ita.growin.domain.auth.dto.request.RefreshTokenRequest; +import ita.growin.domain.auth.dto.response.AuthResponse; +import ita.growin.domain.auth.dto.response.UserDto; +import ita.growin.domain.auth.entity.JwtTokenProvider; +import ita.growin.domain.auth.entity.RefreshToken; +import ita.growin.domain.auth.kakao.KakaoApiClient; +import ita.growin.domain.auth.kakao.KakaoApiClient.KakaoUserInfo; +import ita.growin.domain.auth.repository.RefreshTokenRepository; +import ita.growin.domain.user.constant.LoginType; +import ita.growin.domain.user.constant.UserStatus; +import ita.growin.domain.user.entity.User; +import ita.growin.domain.user.repository.UserRepository; +import ita.growin.global.util.NicknameGenerator; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.hibernate.usertype.UserType; + +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AuthService { + + private final UserRepository userRepository; + private final RefreshTokenRepository refreshTokenRepository; + private final KakaoApiClient kakaoApiClient; + private final JwtTokenProvider jwtTokenProvider; + private final NicknameGenerator nicknameGenerator; + + @Transactional + public AuthResponse kakaoSignup(KakaoSignupRequest request) { + // 1. 카카오 API로 유저 정보 조회 + KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(request.getAccessToken()); + + // 2. 이메일 중복 체크 + if (userRepository.findByEmail(kakaoUser.getKakaoAccount().getEmail()).isPresent()) { + throw new RuntimeException("이미 가입된 이메일입니다."); + } + + // 3. 카카오 ID 중복 체크 + if (userRepository.findByKakaoId(kakaoUser.getId()).isPresent()) { + throw new RuntimeException("이미 가입된 카카오 계정입니다."); + } + + // 4. 닉네임 랜덤 생성 + String randomNickname = nicknameGenerator.generate(); + + // 5. User 엔티티 생성 및 저장 + User user = User.builder() + .email(kakaoUser.getKakaoAccount().getEmail()) + .nickname(randomNickname) + .type(LoginType.KAKAO) + .status(UserStatus.ACTIVE) + .work(request.getWork()) + .interestField(request.getInterestField()) + .target(request.getTarget()) + .build(); + + userRepository.save(user); + + // 6. JWT 토큰 생성 + String accessToken = jwtTokenProvider.generateAccessToken(user); + String refreshToken = jwtTokenProvider.generateRefreshToken(user); + + // 7. Refresh Token 저장 + saveRefreshToken(user.getUserId(), refreshToken); + + // 8. 응답 생성 + UserDto userDto = UserDto.from(user); + + return AuthResponse.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .tokenType("Bearer") + .expiresIn(3600L) + .user(userDto) + .build(); + } + + @Transactional + public AuthResponse kakaoLogin(KakaoLoginRequest request) { + // 1. 카카오 API로 유저 정보 조회 + KakaoApiClient.KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(request.getAccessToken()); + + // 2. DB에서 유저 찾기 + User user = userRepository.findByKakaoId(kakaoUser.getId()) + .orElseThrow(() -> new RuntimeException("가입되지 않은 사용자입니다.")); + + // 3. 탈퇴한 사용자 체크 + if (user.getStatus() == UserStatus.WITHDRAW) { + throw new RuntimeException("탈퇴한 사용자입니다."); + } + + // 4. JWT 토큰 생성 + String accessToken = jwtTokenProvider.generateAccessToken(user); + String refreshToken = jwtTokenProvider.generateRefreshToken(user); + + // 5. 기존 Refresh Token 삭제 후 새로 저장 + refreshTokenRepository.deleteByUserId(user.getUserId()); + saveRefreshToken(user.getUserId(), refreshToken); + + log.info("로그인 성공 - User ID: {}, Email: {}", user.getUserId(), user.getEmail()); + + // 6. 응답 생성 + return AuthResponse.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .tokenType("Bearer") + .expiresIn(3600L) + .user(UserDto.from(user)) + .build(); + } + + @Transactional + public AuthResponse refreshToken(RefreshTokenRequest request) { + String refreshTokenValue = request.getRefreshToken(); + + // 1. Refresh Token 검증 + if (!jwtTokenProvider.validateToken(refreshTokenValue)) { + throw new RuntimeException("유효하지 않은 Refresh Token입니다."); + } + + // 2. DB에서 Refresh Token 확인 + RefreshToken refreshToken = refreshTokenRepository.findByToken(refreshTokenValue) + .orElseThrow(() -> new RuntimeException("등록되지 않은 Refresh Token입니다.")); + + // 3. User 조회 + User user = userRepository.findById(refreshToken.getUserId()) + .orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다.")); + + // 4. 새 토큰 생성 + String newAccessToken = jwtTokenProvider.generateAccessToken(user); + String newRefreshToken = jwtTokenProvider.generateRefreshToken(user); + + // 5. 기존 Refresh Token 삭제 및 새 토큰 저장 + refreshTokenRepository.deleteByToken(refreshTokenValue); + saveRefreshToken(user.getUserId(), newRefreshToken); + + log.info("토큰 갱신 성공 - User ID: {}", user.getUserId()); + + return AuthResponse.builder() + .accessToken(newAccessToken) + .refreshToken(newRefreshToken) + .tokenType("Bearer") + .expiresIn(3600L) + .user(UserDto.from(user)) + .build(); + } + + @Transactional + public void logout(Long userId) { + refreshTokenRepository.deleteByUserId(userId); + log.info("로그아웃 - User ID: {}", userId); + } + + private void saveRefreshToken(Long userId, String token) { + LocalDateTime expiresAt = LocalDateTime.now().plusDays(7); + + RefreshToken refreshToken = RefreshToken.builder() + .userId(userId) + .token(token) + .expiresAt(expiresAt) + .build(); + + refreshTokenRepository.save(refreshToken); + } +} \ No newline at end of file From 51ca0d8e61775283df05e88394035d85e9f45df4 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:27:28 +0900 Subject: [PATCH 05/16] feat : enum --- .../java/ita/growin/domain/user/constant/InterestField.java | 5 +++++ src/main/java/ita/growin/domain/user/constant/LoginType.java | 5 +++++ src/main/java/ita/growin/domain/user/constant/Target.java | 5 +++++ .../java/ita/growin/domain/user/constant/UserStatus.java | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 src/main/java/ita/growin/domain/user/constant/InterestField.java create mode 100644 src/main/java/ita/growin/domain/user/constant/LoginType.java create mode 100644 src/main/java/ita/growin/domain/user/constant/Target.java create mode 100644 src/main/java/ita/growin/domain/user/constant/UserStatus.java diff --git a/src/main/java/ita/growin/domain/user/constant/InterestField.java b/src/main/java/ita/growin/domain/user/constant/InterestField.java new file mode 100644 index 0000000..544098c --- /dev/null +++ b/src/main/java/ita/growin/domain/user/constant/InterestField.java @@ -0,0 +1,5 @@ +package ita.growin.domain.user.constant; + +public enum InterestField { + ECONOMY_MANAGEMENT, IT, HUMAN_EDUCATION_PSYCHOLOGY, DESIGN_ART_MEDIA, NATURE_ENVIRONMENT, NONE +} diff --git a/src/main/java/ita/growin/domain/user/constant/LoginType.java b/src/main/java/ita/growin/domain/user/constant/LoginType.java new file mode 100644 index 0000000..55ba2a1 --- /dev/null +++ b/src/main/java/ita/growin/domain/user/constant/LoginType.java @@ -0,0 +1,5 @@ +package ita.growin.domain.user.constant; + +public enum LoginType { + KAKAO, APPLE +} diff --git a/src/main/java/ita/growin/domain/user/constant/Target.java b/src/main/java/ita/growin/domain/user/constant/Target.java new file mode 100644 index 0000000..52b0553 --- /dev/null +++ b/src/main/java/ita/growin/domain/user/constant/Target.java @@ -0,0 +1,5 @@ +package ita.growin.domain.user.constant; + +public enum Target { + WORK_CAREER, MAJOR_CAREER_EXPLORATION, STARTUP, UNDECIDED +} diff --git a/src/main/java/ita/growin/domain/user/constant/UserStatus.java b/src/main/java/ita/growin/domain/user/constant/UserStatus.java new file mode 100644 index 0000000..4a25d5a --- /dev/null +++ b/src/main/java/ita/growin/domain/user/constant/UserStatus.java @@ -0,0 +1,5 @@ +package ita.growin.domain.user.constant; + +public enum UserStatus { + ACTIVE, WITHDRAW +} From a0a499156a38379996b9d888544a5fe1fc7cc62a Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:28:34 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat=20:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/entity/JwtTokenProvider.java | 96 +++++++++++++++++++ .../domain/auth/entity/RefreshToken.java | 36 +++++++ .../domain/auth/kakao/KakaoApiClient.java | 73 ++++++++++++++ .../repository/RefreshTokenRepository.java | 12 +++ 4 files changed, 217 insertions(+) create mode 100644 src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java create mode 100644 src/main/java/ita/growin/domain/auth/entity/RefreshToken.java create mode 100644 src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java create mode 100644 src/main/java/ita/growin/domain/auth/repository/RefreshTokenRepository.java diff --git a/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java b/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java new file mode 100644 index 0000000..46177e1 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java @@ -0,0 +1,96 @@ +package ita.growin.domain.auth.entity; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import ita.growin.domain.user.entity.User; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.security.Key; +import java.util.Date; + +@Slf4j +@Component +public class JwtTokenProvider { + + private final Key key; + private final long accessTokenValidity; + private final long refreshTokenValidity; + + public JwtTokenProvider( + @Value("${jwt.secret}") String secret, + @Value("${jwt.access-token-validity}") long accessTokenValidity, + @Value("${jwt.refresh-token-validity}") long refreshTokenValidity + ) { + this.key = Keys.hmacShaKeyFor(secret.getBytes()); + this.accessTokenValidity = accessTokenValidity; + this.refreshTokenValidity = refreshTokenValidity; + } + + // Access Token 생성 + public String generateAccessToken(User user) { + Date now = new Date(); + Date validity = new Date(now.getTime() + accessTokenValidity); + + return Jwts.builder() + .setSubject(user.getUserId().toString()) + .claim("email", user.getEmail()) + .claim("nickname", user.getNickname()) + .claim("type", user.getType().name()) + .setIssuedAt(now) + .setExpiration(validity) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + // Refresh Token 생성 + public String generateRefreshToken(User user) { + Date now = new Date(); + Date validity = new Date(now.getTime() + refreshTokenValidity); + + return Jwts.builder() + .setSubject(user.getUserId().toString()) + .setIssuedAt(now) + .setExpiration(validity) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + // JWT 검증 + public boolean validateToken(String token) { + try { + Jws claims = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + + return !claims.getBody().getExpiration().before(new Date()); + } catch (JwtException | IllegalArgumentException e) { + log.error("JWT 검증 실패: {}", e.getMessage()); + return false; + } + } + + // User ID 추출 + public Long getUserId(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody(); + + return Long.parseLong(claims.getSubject()); + } + + // 만료 시간 추출 + public Date getExpirationDate(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody(); + + return claims.getExpiration(); + } +} diff --git a/src/main/java/ita/growin/domain/auth/entity/RefreshToken.java b/src/main/java/ita/growin/domain/auth/entity/RefreshToken.java new file mode 100644 index 0000000..5d83f19 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/entity/RefreshToken.java @@ -0,0 +1,36 @@ +package ita.growin.domain.auth.entity; + +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "refresh_tokens") +@Builder +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class RefreshToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id", nullable = false) + private Long userId; + + @Column(nullable = false, length = 500) + private String token; + + @Column(name = "expires_at", nullable = false) + private LocalDateTime expiresAt; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @PrePersist + protected void onCreate() { + createdAt = LocalDateTime.now(); + } +} diff --git a/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java b/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java new file mode 100644 index 0000000..2fd4827 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java @@ -0,0 +1,73 @@ +package ita.growin.domain.auth.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; +import org.springframework.http.*; +import org.springframework.web.client.RestTemplate; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class KakaoApiClient { + + private final RestTemplate restTemplate; + private static final String KAKAO_USER_INFO_URL = "https://kapi.kakao.com/v2/user/me"; + + public KakaoApiClient() { + this.restTemplate = new RestTemplate(); + } + + public KakaoUserInfo getUserInfo(String accessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + HttpEntity entity = new HttpEntity<>(headers); + + try { + ResponseEntity response = restTemplate.exchange( + KAKAO_USER_INFO_URL, + HttpMethod.GET, + entity, + KakaoUserInfo.class + ); + + log.info("카카오 사용자 정보 조회 성공: {}", response.getBody()); + return response.getBody(); + + } catch (Exception e) { + log.error("카카오 API 호출 실패", e); + throw new RuntimeException("카카오 사용자 정보 조회 실패", e); + } + } + + @Getter + @Setter + public static class KakaoUserInfo { + private Long id; + + @JsonProperty("kakao_account") + private KakaoAccount kakaoAccount; + + @Getter + @Setter + public static class KakaoAccount { + private String email; + + @JsonProperty("email_needs_agreement") + private Boolean emailNeedsAgreement; + + private Profile profile; + + @Getter + @Setter + public static class Profile { + private String nickname; + } + } + } +} diff --git a/src/main/java/ita/growin/domain/auth/repository/RefreshTokenRepository.java b/src/main/java/ita/growin/domain/auth/repository/RefreshTokenRepository.java new file mode 100644 index 0000000..0101015 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/repository/RefreshTokenRepository.java @@ -0,0 +1,12 @@ +package ita.growin.domain.auth.repository; + +import ita.growin.domain.auth.entity.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends JpaRepository { + Optional findByToken(String token); + void deleteByUserId(Long userId); + void deleteByToken(String token); +} From 53f7df8d3486799f05760d46083dc0ba81221990 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:29:07 +0900 Subject: [PATCH 07/16] feat : security filter, config --- .../domain/auth/kakao/KakaoUserInfo.java | 29 +++++++++ .../security/JwtAuthenticationFilter.java | 59 +++++++++++++++++++ .../domain/auth/security/SecurityConfig.java | 34 +++++++++++ 3 files changed, 122 insertions(+) create mode 100644 src/main/java/ita/growin/domain/auth/kakao/KakaoUserInfo.java create mode 100644 src/main/java/ita/growin/domain/auth/security/JwtAuthenticationFilter.java create mode 100644 src/main/java/ita/growin/domain/auth/security/SecurityConfig.java diff --git a/src/main/java/ita/growin/domain/auth/kakao/KakaoUserInfo.java b/src/main/java/ita/growin/domain/auth/kakao/KakaoUserInfo.java new file mode 100644 index 0000000..edc915e --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/kakao/KakaoUserInfo.java @@ -0,0 +1,29 @@ +package ita.growin.domain.auth.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +public class KakaoUserInfo { + private Long id; + + @JsonProperty("kakao_account") + private KakaoAccount kakaoAccount; + + @Getter + public static class KakaoAccount { + private String email; + + @JsonProperty("email_needs_agreement") + private Boolean emailNeedsAgreement; + + private Profile profile; + + @Getter + @Setter + public static class Profile { + private String nickname; + } + } +} diff --git a/src/main/java/ita/growin/domain/auth/security/JwtAuthenticationFilter.java b/src/main/java/ita/growin/domain/auth/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..bc6b78b --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/security/JwtAuthenticationFilter.java @@ -0,0 +1,59 @@ +package ita.growin.domain.auth.security; + +import ita.growin.domain.auth.entity.JwtTokenProvider; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Collections; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { + + String token = resolveToken(request); + + if (token != null && jwtTokenProvider.validateToken(token)) { + Long userId = jwtTokenProvider.getUserId(token); + + // Request에 userId 추가 + request.setAttribute("userId", userId); + + // Security Context에 인증 정보 저장 + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList()); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + log.debug("JWT 인증 성공 - User ID: {}", userId); + } + + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (bearerToken != null && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } +} diff --git a/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java b/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java new file mode 100644 index 0000000..a3e0d79 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java @@ -0,0 +1,34 @@ +package ita.growin.domain.auth.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf().disable() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers("/api/v1/auth/**").permitAll() + .anyRequest().authenticated() + .and() + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } +} \ No newline at end of file From 04d21560df31242d755f7b28d4753e9f1f1fac01 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:29:22 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat=20:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../growin/global/util/NicknameGenerator.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/ita/growin/global/util/NicknameGenerator.java diff --git a/src/main/java/ita/growin/global/util/NicknameGenerator.java b/src/main/java/ita/growin/global/util/NicknameGenerator.java new file mode 100644 index 0000000..4a905ef --- /dev/null +++ b/src/main/java/ita/growin/global/util/NicknameGenerator.java @@ -0,0 +1,38 @@ +package ita.growin.global.util; + +import ita.growin.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Random; + +@Component +@RequiredArgsConstructor +public class NicknameGenerator { + + private final UserRepository userRepository; + private final Random random = new Random(); + + public String generate() { + String nickname; + int attempts = 0; + int maxAttempts = 100; + + do { + nickname = generateRandomNickname(); + attempts++; + + if (attempts >= maxAttempts) { + // UUID 기반으로 전환 + nickname = "사용자" + System.currentTimeMillis(); + } + } while (userRepository.existsByNickname(nickname)); + + return nickname; + } + + private String generateRandomNickname() { + int number = random.nextInt(10000); + return String.format("사용자%04d", number); + } +} From 2dda392603d49b19f9f0854176b481df487c9901 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 3 Nov 2025 22:29:33 +0900 Subject: [PATCH 09/16] =?UTF-8?q?feat=20:=20=EC=9C=A0=EC=A0=80=20repositor?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ita/growin/GrowinApplication.java | 2 ++ .../domain/user/repository/UserRepository.java | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 src/main/java/ita/growin/domain/user/repository/UserRepository.java diff --git a/src/main/java/ita/growin/GrowinApplication.java b/src/main/java/ita/growin/GrowinApplication.java index 4b93e60..9f46f60 100644 --- a/src/main/java/ita/growin/GrowinApplication.java +++ b/src/main/java/ita/growin/GrowinApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class GrowinApplication { public static void main(String[] args) { diff --git a/src/main/java/ita/growin/domain/user/repository/UserRepository.java b/src/main/java/ita/growin/domain/user/repository/UserRepository.java new file mode 100644 index 0000000..3a1a25c --- /dev/null +++ b/src/main/java/ita/growin/domain/user/repository/UserRepository.java @@ -0,0 +1,13 @@ +package ita.growin.domain.user.repository; + +import ita.growin.domain.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserRepository extends JpaRepository { + Optional findByKakaoId(Long kakaoId); + Optional findByEmail(String email); + boolean existsByNickname(String nickname); +} + From 0b4f4c5e9fa2b7a7cada901aebb220c0ab4e5197 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Sat, 8 Nov 2025 17:20:06 +0900 Subject: [PATCH 10/16] =?UTF-8?q?setting=20:=20docker=20db=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-db.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docker-db.yml diff --git a/docker-db.yml b/docker-db.yml new file mode 100644 index 0000000..c927dd9 --- /dev/null +++ b/docker-db.yml @@ -0,0 +1,32 @@ +version: '3.8' + +services: + mysql: + image: mysql:8.3 + container_name: solitour-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: 1234 + MYSQL_DATABASE: growin + MYSQL_USER: growin + MYSQL_PASSWORD: 1234 + TZ: Asia/Seoul + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + - ./mysql.cnf:/etc/mysql/conf.d/my.cnf + - ./initdb/init.sql:/docker-entrypoint-initdb.d/init.sql:ro + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + platform: linux/x86_64 + entrypoint: ["/bin/sh", "-c", "chmod 644 /etc/mysql/conf.d/my.cnf && docker-entrypoint.sh mysqld"] + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p1234"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + mysql_data: \ No newline at end of file From ea5b7e78ceabc2d73d6139804319a9f95eeb9a0b Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Sat, 8 Nov 2025 17:20:53 +0900 Subject: [PATCH 11/16] =?UTF-8?q?refactor=20:=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yaml | 9 ------ src/main/resources/application-local.yaml | 15 --------- src/main/resources/application.yaml | 9 ------ src/main/resources/application.yml | 39 +++++++++++++++++++++++ 4 files changed, 39 insertions(+), 33 deletions(-) delete mode 100644 src/main/resources/application-dev.yaml delete mode 100644 src/main/resources/application-local.yaml delete mode 100644 src/main/resources/application.yaml create mode 100644 src/main/resources/application.yml diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml deleted file mode 100644 index b7c8c8d..0000000 --- a/src/main/resources/application-dev.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spring: - config: - activate: - on-profile: "dev" - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: ${DEV_DB_URL} - username: ${DEV_DB_USERNAME} - password: ${DEV_DB_PASSWORD} diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml deleted file mode 100644 index 97a0708..0000000 --- a/src/main/resources/application-local.yaml +++ /dev/null @@ -1,15 +0,0 @@ -spring: - config: - activate: - on-profile: "local" - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: ${LOCAL_DB_URL} - username: ${LOCAL_DB_USERNAME} - password: ${LOCAL_DB_PASSWORD} - - jpa: - show-sql: true - open-in-view: false - hibernate: - ddl-auto: create-drop diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml deleted file mode 100644 index 4bc836b..0000000 --- a/src/main/resources/application.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spring: - application: - name: growin - - profiles: - group: - local: "local" - dev: "dev" - test: "test" \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..21ceded --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,39 @@ +spring: + application: + name: growin + + profiles: + group: + local: "local" + dev: "dev" + test: "test" + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/growin?serverTimezone=Asia/Seoul&characterEncoding=UTF-8 + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + + jpa: + hibernate: + ddl-auto: update # 개발: update, 운영: validate 또는 none + properties: + hibernate: + format_sql: true + show_sql: true + dialect: org.hibernate.dialect.MySQLDialect + show-sql: true + + +jwt: + secret: ${JWT_SECRET} + access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY} + refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY} + +kakao: + client-id: ${KAKAO_CLIENT_ID} + redirect-uri: ${KAKAO_REDIRECT_URI} + +logging: + level: + org.hibernate.SQL: debug From 70a64d604c0399a2fa5e7f0c15e3580ae7fd9bff Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Sat, 8 Nov 2025 17:21:13 +0900 Subject: [PATCH 12/16] =?UTF-8?q?refactor=20:=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=20=EA=B2=80=EC=A6=9D=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/service/AuthService.java | 21 +++++++------------ .../user/repository/UserRepository.java | 1 - 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/ita/growin/domain/auth/service/AuthService.java b/src/main/java/ita/growin/domain/auth/service/AuthService.java index f13b999..fa2184f 100644 --- a/src/main/java/ita/growin/domain/auth/service/AuthService.java +++ b/src/main/java/ita/growin/domain/auth/service/AuthService.java @@ -38,23 +38,18 @@ public class AuthService { @Transactional public AuthResponse kakaoSignup(KakaoSignupRequest request) { - // 1. 카카오 API로 유저 정보 조회 + // 카카오 API로 유저 정보 조회 KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(request.getAccessToken()); - // 2. 이메일 중복 체크 + // 이메일 중복 체크 if (userRepository.findByEmail(kakaoUser.getKakaoAccount().getEmail()).isPresent()) { throw new RuntimeException("이미 가입된 이메일입니다."); } - // 3. 카카오 ID 중복 체크 - if (userRepository.findByKakaoId(kakaoUser.getId()).isPresent()) { - throw new RuntimeException("이미 가입된 카카오 계정입니다."); - } - - // 4. 닉네임 랜덤 생성 + // 닉네임 랜덤 생성 String randomNickname = nicknameGenerator.generate(); - // 5. User 엔티티 생성 및 저장 + // User 엔티티 생성 및 저장 User user = User.builder() .email(kakaoUser.getKakaoAccount().getEmail()) .nickname(randomNickname) @@ -67,14 +62,14 @@ public AuthResponse kakaoSignup(KakaoSignupRequest request) { userRepository.save(user); - // 6. JWT 토큰 생성 + // JWT 토큰 생성 String accessToken = jwtTokenProvider.generateAccessToken(user); String refreshToken = jwtTokenProvider.generateRefreshToken(user); - // 7. Refresh Token 저장 + // Refresh Token 저장 saveRefreshToken(user.getUserId(), refreshToken); - // 8. 응답 생성 + // 응답 생성 UserDto userDto = UserDto.from(user); return AuthResponse.builder() @@ -92,7 +87,7 @@ public AuthResponse kakaoLogin(KakaoLoginRequest request) { KakaoApiClient.KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(request.getAccessToken()); // 2. DB에서 유저 찾기 - User user = userRepository.findByKakaoId(kakaoUser.getId()) + User user = userRepository.findByEmail(kakaoUser.getKakaoAccount().getEmail()) .orElseThrow(() -> new RuntimeException("가입되지 않은 사용자입니다.")); // 3. 탈퇴한 사용자 체크 diff --git a/src/main/java/ita/growin/domain/user/repository/UserRepository.java b/src/main/java/ita/growin/domain/user/repository/UserRepository.java index 3a1a25c..a3a4b89 100644 --- a/src/main/java/ita/growin/domain/user/repository/UserRepository.java +++ b/src/main/java/ita/growin/domain/user/repository/UserRepository.java @@ -6,7 +6,6 @@ import java.util.Optional; public interface UserRepository extends JpaRepository { - Optional findByKakaoId(Long kakaoId); Optional findByEmail(String email); boolean existsByNickname(String nickname); } From 9917b77b80801e430376dda9b65edcb6ebef3d79 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Sat, 8 Nov 2025 17:22:06 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feat=20:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A7=81=ED=81=AC=20api=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthController.java | 29 +++++++++++++++++++ .../domain/auth/entity/JwtTokenProvider.java | 3 +- .../domain/auth/security/SecurityConfig.java | 25 +++++++++++----- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/main/java/ita/growin/domain/auth/controller/AuthController.java b/src/main/java/ita/growin/domain/auth/controller/AuthController.java index 520808f..daee029 100644 --- a/src/main/java/ita/growin/domain/auth/controller/AuthController.java +++ b/src/main/java/ita/growin/domain/auth/controller/AuthController.java @@ -6,12 +6,18 @@ import ita.growin.domain.auth.dto.response.AuthResponse; import ita.growin.domain.auth.service.AuthService; import ita.growin.global.response.APIResponse; +import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; @Slf4j @RestController @@ -19,8 +25,31 @@ @RequiredArgsConstructor public class AuthController { + @Value("${kakao.client-id}") + private String kakaoClientId; + + @Value("${kakao.redirect-uri}") + private String kakaoRedirectUri; + private final AuthService authService; + @GetMapping("/kakao") + public ResponseEntity> kakaoLogin() throws IOException { + String kakaoAuthUrl = UriComponentsBuilder + .fromUriString("https://kauth.kakao.com/oauth/authorize") + .queryParam("client_id", kakaoClientId) + .queryParam("redirect_uri", kakaoRedirectUri) + .queryParam("response_type", "code") + .queryParam("scope", "profile_nickname,account_email") + .build() + .toUriString(); + + Map response = new HashMap<>(); + response.put("url", kakaoAuthUrl); + + return ResponseEntity.ok(response); + } + @PostMapping("/kakao/signup") public ResponseEntity> kakaoSignup( @Valid @RequestBody KakaoSignupRequest request diff --git a/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java b/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java index 46177e1..4599edc 100644 --- a/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java +++ b/src/main/java/ita/growin/domain/auth/entity/JwtTokenProvider.java @@ -3,8 +3,9 @@ import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import ita.growin.domain.user.entity.User; -import lombok.Value; + import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.security.Key; diff --git a/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java b/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java index a3e0d79..68103bf 100644 --- a/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java +++ b/src/main/java/ita/growin/domain/auth/security/SecurityConfig.java @@ -6,6 +6,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -19,14 +20,22 @@ public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http - .csrf().disable() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .authorizeRequests() - .antMatchers("/api/v1/auth/**").permitAll() - .anyRequest().authenticated() - .and() + // CSRF 비활성화 (최신 문법) + .csrf(AbstractHttpConfigurer::disable) + + // 세션 관리 설정 (최신 문법) + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + + // 요청 인증 설정 (최신 문법) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/v1/auth/**").permitAll() + .requestMatchers("/oauth2/callback/kakao").permitAll() + .anyRequest().authenticated() + ) + + // JWT 필터 추가 .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); From bbdf4a1752926f1d2f73a4bbd167ef4798c6b1d5 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Sat, 8 Nov 2025 20:52:16 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor=20:=20access=20token=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/dto/request/KakaoSignupRequest.java | 4 +- .../auth/dto/response/KakaoAuthToken.java | 18 ++++ .../auth/dto/response/KakaoTokenResponse.java | 24 ++++++ .../domain/auth/kakao/KakaoApiClient.java | 84 ++++++++++++++++--- .../domain/auth/service/AuthService.java | 2 +- 5 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 src/main/java/ita/growin/domain/auth/dto/response/KakaoAuthToken.java create mode 100644 src/main/java/ita/growin/domain/auth/dto/response/KakaoTokenResponse.java diff --git a/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java b/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java index d1f4c05..6f9091c 100644 --- a/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java +++ b/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java @@ -11,8 +11,8 @@ @Getter public class KakaoSignupRequest { - @NotBlank(message = "Access Token은 필수입니다.") - private String accessToken; + @NotBlank(message = "Code는 필수입니다.") + private String code; @NotNull(message = "직업 정보는 필수입니다.") private Work work; diff --git a/src/main/java/ita/growin/domain/auth/dto/response/KakaoAuthToken.java b/src/main/java/ita/growin/domain/auth/dto/response/KakaoAuthToken.java new file mode 100644 index 0000000..a1de7d6 --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/response/KakaoAuthToken.java @@ -0,0 +1,18 @@ +package ita.growin.domain.auth.dto.response; + +import lombok.Getter; + +@Getter +public class KakaoAuthToken { + /** 토큰 요청에 필요한 인가 코드 */ + private String code; + + /** 인증 실패 시 반환되는 에러 코드 */ + private String error; + + /** 인증 실패 시 반환되는 에러 메시지 */ + private String error_description; + + /** CSRF 방지용 state 값 (선택적으로 사용) */ + private String state; +} diff --git a/src/main/java/ita/growin/domain/auth/dto/response/KakaoTokenResponse.java b/src/main/java/ita/growin/domain/auth/dto/response/KakaoTokenResponse.java new file mode 100644 index 0000000..19c10ad --- /dev/null +++ b/src/main/java/ita/growin/domain/auth/dto/response/KakaoTokenResponse.java @@ -0,0 +1,24 @@ +package ita.growin.domain.auth.dto.response; + +import lombok.Getter; + +@Getter +public class KakaoTokenResponse { + /** 토큰 타입 (보통 "bearer") */ + private String token_type; + + /** 사용자 액세스 토큰 */ + private String access_token; + + /** 액세스 토큰 만료 시간(초 단위) */ + private Integer expires_in; + + /** 리프레시 토큰 */ + private String refresh_token; + + /** 리프레시 토큰 만료 시간(초 단위) */ + private Integer refresh_token_expires_in; + + /** 인증된 정보 범위(scope). 공백으로 구분됨 */ + private String scope; +} diff --git a/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java b/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java index 2fd4827..4ee520d 100644 --- a/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java +++ b/src/main/java/ita/growin/domain/auth/kakao/KakaoApiClient.java @@ -1,9 +1,15 @@ package ita.growin.domain.auth.kakao; import com.fasterxml.jackson.annotation.JsonProperty; +import ita.growin.domain.auth.dto.response.KakaoAuthToken; +import ita.growin.domain.auth.dto.response.KakaoTokenResponse; +import java.util.List; import lombok.Getter; import lombok.Setter; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import lombok.extern.slf4j.Slf4j; @@ -14,35 +20,87 @@ @Component public class KakaoApiClient { + @Value("${kakao.client-id}") + private String clientId; + + @Value("${kakao.redirect-uri}") + private String redirectUri; private final RestTemplate restTemplate; private static final String KAKAO_USER_INFO_URL = "https://kapi.kakao.com/v2/user/me"; + private static final String KAKAO_TOKEN_URL = "https://kauth.kakao.com/oauth/token"; + private static final String KAKAO_AUTH_CODE_URL ="https://kauth.kakao.com/oauth/authorize"; public KakaoApiClient() { this.restTemplate = new RestTemplate(); } - public KakaoUserInfo getUserInfo(String accessToken) { + public KakaoUserInfo getUserInfo(String code) { + try { +// ResponseEntity authorizeCode =requestAuthorizeCode(code); + KakaoTokenResponse tokenResponse = requestKakaoAccessToken(code); + ResponseEntity kakaoUserInfo = requestUserInfo(tokenResponse.getAccess_token()); + + return kakaoUserInfo.getBody(); + + } catch (Exception e) { + log.error("카카오 API 호출 실패", e); + throw new RuntimeException("카카오 사용자 정보 조회 실패", e); + } + } + + private ResponseEntity requestUserInfo(String accessToken) { HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntity entity = new HttpEntity<>(headers); - try { - ResponseEntity response = restTemplate.exchange( - KAKAO_USER_INFO_URL, - HttpMethod.GET, - entity, - KakaoUserInfo.class - ); + ResponseEntity response = restTemplate.exchange( + KAKAO_USER_INFO_URL, + HttpMethod.GET, + entity, + KakaoUserInfo.class + ); - log.info("카카오 사용자 정보 조회 성공: {}", response.getBody()); - return response.getBody(); + return response; - } catch (Exception e) { - log.error("카카오 API 호출 실패", e); - throw new RuntimeException("카카오 사용자 정보 조회 실패", e); + } + + private KakaoTokenResponse requestKakaoAccessToken(String code) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("grant_type", "authorization_code"); + form.add("client_id", clientId); + form.add("redirect_uri", redirectUri); + form.add("code", code); + HttpEntity> req = new HttpEntity<>(form, headers); + + ResponseEntity res = restTemplate.postForEntity( + KAKAO_TOKEN_URL, req, KakaoTokenResponse.class); + + if (!res.getStatusCode().is2xxSuccessful() || res.getBody() == null) { + throw new IllegalStateException("카카오 토큰 발급 실패: " + res.getStatusCode()); } + return res.getBody(); + } + + private ResponseEntity requestAuthorizeCode(String code) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("grant_type", "authorization_code"); + form.add("client_id", clientId); + form.add("redirect_uri", redirectUri); + form.add("code", code); + + HttpEntity> req = new HttpEntity<>(form, headers); + return restTemplate.postForEntity(KAKAO_AUTH_CODE_URL, req, KakaoAuthToken.class); } @Getter diff --git a/src/main/java/ita/growin/domain/auth/service/AuthService.java b/src/main/java/ita/growin/domain/auth/service/AuthService.java index fa2184f..95d746d 100644 --- a/src/main/java/ita/growin/domain/auth/service/AuthService.java +++ b/src/main/java/ita/growin/domain/auth/service/AuthService.java @@ -39,7 +39,7 @@ public class AuthService { @Transactional public AuthResponse kakaoSignup(KakaoSignupRequest request) { // 카카오 API로 유저 정보 조회 - KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(request.getAccessToken()); + KakaoUserInfo kakaoUser = kakaoApiClient.getUserInfo(request.getCode()); // 이메일 중복 체크 if (userRepository.findByEmail(kakaoUser.getKakaoAccount().getEmail()).isPresent()) { From faf307bcc4651e0bfaaa41cda554983d8fb0d374 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Tue, 2 Dec 2025 22:52:00 +0900 Subject: [PATCH 15/16] =?UTF-8?q?refactor=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20db=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-db.yml => db-compose.yml | 6 +++--- .../ita/growin/domain/auth/controller/AuthController.java | 4 ---- .../java/ita/growin/domain/auth/service/AuthService.java | 3 --- 3 files changed, 3 insertions(+), 10 deletions(-) rename docker-db.yml => db-compose.yml (89%) diff --git a/docker-db.yml b/db-compose.yml similarity index 89% rename from docker-db.yml rename to db-compose.yml index c927dd9..5688bb6 100644 --- a/docker-db.yml +++ b/db-compose.yml @@ -3,12 +3,12 @@ version: '3.8' services: mysql: image: mysql:8.3 - container_name: solitour-mysql + container_name: hanipman-mysql restart: always environment: MYSQL_ROOT_PASSWORD: 1234 - MYSQL_DATABASE: growin - MYSQL_USER: growin + MYSQL_DATABASE: hanipman + MYSQL_USER: hanipman MYSQL_PASSWORD: 1234 TZ: Asia/Seoul ports: diff --git a/src/main/java/ita/growin/domain/auth/controller/AuthController.java b/src/main/java/ita/growin/domain/auth/controller/AuthController.java index daee029..a271ef0 100644 --- a/src/main/java/ita/growin/domain/auth/controller/AuthController.java +++ b/src/main/java/ita/growin/domain/auth/controller/AuthController.java @@ -54,7 +54,6 @@ public ResponseEntity> kakaoLogin() throws IOException { public ResponseEntity> kakaoSignup( @Valid @RequestBody KakaoSignupRequest request ) { - log.info("카카오 회원가입 요청"); AuthResponse response = authService.kakaoSignup(request); return ResponseEntity.status(HttpStatus.CREATED) .body(APIResponse.success(response)); @@ -64,7 +63,6 @@ public ResponseEntity> kakaoSignup( public ResponseEntity> kakaoLogin( @Valid @RequestBody KakaoLoginRequest request ) { - log.info("카카오 로그인 요청"); AuthResponse response = authService.kakaoLogin(request); return ResponseEntity.ok(APIResponse.success(response)); } @@ -73,7 +71,6 @@ public ResponseEntity> kakaoLogin( public ResponseEntity> refreshToken( @Valid @RequestBody RefreshTokenRequest request ) { - log.info("토큰 갱신 요청"); AuthResponse response = authService.refreshToken(request); return ResponseEntity.ok(APIResponse.success(response)); } @@ -82,7 +79,6 @@ public ResponseEntity> refreshToken( public ResponseEntity> logout( @RequestAttribute("userId") Long userId ) { - log.info("로그아웃 요청 - User ID: {}", userId); authService.logout(userId); return ResponseEntity.ok(APIResponse.success(null)); } diff --git a/src/main/java/ita/growin/domain/auth/service/AuthService.java b/src/main/java/ita/growin/domain/auth/service/AuthService.java index 95d746d..11ef6ca 100644 --- a/src/main/java/ita/growin/domain/auth/service/AuthService.java +++ b/src/main/java/ita/growin/domain/auth/service/AuthService.java @@ -46,9 +46,6 @@ public AuthResponse kakaoSignup(KakaoSignupRequest request) { throw new RuntimeException("이미 가입된 이메일입니다."); } - // 닉네임 랜덤 생성 - String randomNickname = nicknameGenerator.generate(); - // User 엔티티 생성 및 저장 User user = User.builder() .email(kakaoUser.getKakaoAccount().getEmail()) From 8471c760660287a8f6caf5494759462c73864e3a Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Tue, 2 Dec 2025 22:52:47 +0900 Subject: [PATCH 16/16] =?UTF-8?q?refactor=20:=20=EA=B8=B0=ED=9A=8D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/dto/request/KakaoSignupRequest.java | 12 +++++----- .../domain/auth/dto/response/UserDto.java | 12 ++++------ .../domain/auth/service/AuthService.java | 7 +++--- .../growin/domain/user/constant/Location.java | 4 ++++ .../domain/user/constant/LoginType.java | 2 +- .../domain/user/constant/UserStatus.java | 2 +- .../ita/growin/domain/user/entity/User.java | 23 ++++++------------- 7 files changed, 27 insertions(+), 35 deletions(-) create mode 100644 src/main/java/ita/growin/domain/user/constant/Location.java diff --git a/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java b/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java index 6f9091c..6d751ad 100644 --- a/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java +++ b/src/main/java/ita/growin/domain/auth/dto/request/KakaoSignupRequest.java @@ -14,14 +14,14 @@ public class KakaoSignupRequest { @NotBlank(message = "Code는 필수입니다.") private String code; - @NotNull(message = "직업 정보는 필수입니다.") - private Work work; + @NotNull(message = "동네 정보는 필수입니다.") + private String location; - @NotNull(message = "관심분야는 필수입니다.") - private InterestField interestField; + @NotNull(message = "닉네임은 필수입니다.") + private String nickname; - @NotNull(message = "목표는 필수입니다.") - private Target target; + @NotNull(message = "전화번호는 필수입니다.") + private String phone; private String deviceToken; } \ No newline at end of file diff --git a/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java b/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java index 2c52c13..8fcb198 100644 --- a/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java +++ b/src/main/java/ita/growin/domain/auth/dto/response/UserDto.java @@ -17,22 +17,20 @@ public class UserDto { private String nickname; private LoginType type; private UserStatus status; - private Work work; - private InterestField interestField; - private Target target; + private String location; + private String phone; private LocalDateTime createdAt; private Boolean isNewUser; public static UserDto from(User user) { - return UserDto.builder() + return UserDto.builder() .userId(user.getUserId()) .email(user.getEmail()) .nickname(user.getNickname()) .type(user.getType()) .status(user.getStatus()) - .work(user.getWork()) - .interestField(user.getInterestField()) - .target(user.getTarget()) + .phone(user.getPhone()) + .location(user.getLocation()) .createdAt(user.getCreatedAt()) .isNewUser(false) .build(); diff --git a/src/main/java/ita/growin/domain/auth/service/AuthService.java b/src/main/java/ita/growin/domain/auth/service/AuthService.java index 11ef6ca..e913f1d 100644 --- a/src/main/java/ita/growin/domain/auth/service/AuthService.java +++ b/src/main/java/ita/growin/domain/auth/service/AuthService.java @@ -49,12 +49,11 @@ public AuthResponse kakaoSignup(KakaoSignupRequest request) { // User 엔티티 생성 및 저장 User user = User.builder() .email(kakaoUser.getKakaoAccount().getEmail()) - .nickname(randomNickname) + .nickname(request.getNickname()) + .location(request.getLocation()) .type(LoginType.KAKAO) + .phone(request.getPhone()) .status(UserStatus.ACTIVE) - .work(request.getWork()) - .interestField(request.getInterestField()) - .target(request.getTarget()) .build(); userRepository.save(user); diff --git a/src/main/java/ita/growin/domain/user/constant/Location.java b/src/main/java/ita/growin/domain/user/constant/Location.java new file mode 100644 index 0000000..12c3d43 --- /dev/null +++ b/src/main/java/ita/growin/domain/user/constant/Location.java @@ -0,0 +1,4 @@ +package ita.growin.domain.user.constant; + +public enum Location { +} diff --git a/src/main/java/ita/growin/domain/user/constant/LoginType.java b/src/main/java/ita/growin/domain/user/constant/LoginType.java index 55ba2a1..7a3a235 100644 --- a/src/main/java/ita/growin/domain/user/constant/LoginType.java +++ b/src/main/java/ita/growin/domain/user/constant/LoginType.java @@ -1,5 +1,5 @@ package ita.growin.domain.user.constant; public enum LoginType { - KAKAO, APPLE + KAKAO, GOOGLE } diff --git a/src/main/java/ita/growin/domain/user/constant/UserStatus.java b/src/main/java/ita/growin/domain/user/constant/UserStatus.java index 4a25d5a..1dd6ab7 100644 --- a/src/main/java/ita/growin/domain/user/constant/UserStatus.java +++ b/src/main/java/ita/growin/domain/user/constant/UserStatus.java @@ -1,5 +1,5 @@ package ita.growin.domain.user.constant; public enum UserStatus { - ACTIVE, WITHDRAW + ACTIVE, INACTIVE, WITHDRAW } diff --git a/src/main/java/ita/growin/domain/user/entity/User.java b/src/main/java/ita/growin/domain/user/entity/User.java index 28604d9..818706f 100644 --- a/src/main/java/ita/growin/domain/user/entity/User.java +++ b/src/main/java/ita/growin/domain/user/entity/User.java @@ -1,15 +1,12 @@ package ita.growin.domain.user.entity; -import ita.growin.domain.event.entity.Event; -import ita.growin.domain.user.constant.*; +import ita.growin.domain.user.constant.LoginType; +import ita.growin.domain.user.constant.UserStatus; import ita.growin.global.entity.BaseEntity; import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.Comment; -import java.util.ArrayList; -import java.util.List; - @Entity @Table(name = "users") @Getter @@ -26,6 +23,9 @@ public class User extends BaseEntity { @Column(nullable = false, length = 50) private String email; + @Column(length = 50) + private String phone; + @Enumerated(EnumType.STRING) @Column(nullable = false) private LoginType type; @@ -37,15 +37,6 @@ public class User extends BaseEntity { @Column(nullable = false, length = 100) private String nickname; - @Enumerated(EnumType.STRING) - private Work work; - - @Enumerated(EnumType.STRING) - private InterestField interestField; - - @Enumerated(EnumType.STRING) - private Target target; - - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) - private List events = new ArrayList<>(); + @Column(nullable = false, length = 100) + private String location; }