diff --git a/.github/workflows/todo_bot.yml b/.github/workflows/todo_bot.yml
new file mode 100644
index 00000000..5a47095f
--- /dev/null
+++ b/.github/workflows/todo_bot.yml
@@ -0,0 +1,21 @@
+name: "Run TODO to Issue"
+on:
+ push:
+ workflow_dispatch:
+ inputs:
+ MANUAL_COMMIT_REF:
+ description: "The SHA of the commit to get the diff for"
+ required: true
+ MANUAL_BASE_REF:
+ description: "By default, the commit entered above is compared to the one directly before it; to go back further, enter an earlier SHA here"
+ required: false
+jobs:
+ build:
+ runs-on: "ubuntu-latest"
+ steps:
+ - uses: "actions/checkout@v3"
+ - name: "TODO to Issue"
+ uses: "alstr/todo-to-issue-action@master"
+ env:
+ MANUAL_COMMIT_REF: ${{ inputs.MANUAL_COMMIT_REF }}
+ MANUAL_BASE_REF: ${{ inputs.MANUAL_BASE_REF }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 55608a8e..7ca0904d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,6 @@ out/
### VS Code ###
.vscode/
+
+### test ###
+src/main/java/com/kcy/fitapet/test/
diff --git a/README.md b/README.md
index e546f405..20e274e2 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@
## Tech Stack
### Framework & Library
-- JDK 11
+- JDK 17
- SpringBoot 3.1.0
- SpringBoot Security
- Spring Data JPA
@@ -65,16 +65,16 @@
- Goorm IDE (for DB Server)
## Project Check List
-- [ ] 실제 서비스를 공개적으로 배포하고 운영하는 경험을 해보았다.
-- [ ] 유저의 피드백에 따라 성능/사용성을 개선하고 신규 기능을 추가해보았다.
-- [ ] 발견되는 버그와 개선사항들을 정리하고 쌓인 이슈들을 체계적으로 관리해보았다.
-- [ ] 코드를 지속적으로 리팩토링하고 디자인 패턴을 적용해보았다.
-- [ ] 위의 시도에서 더 좋은 설계와 더 빠른 개발 사이의 트레이드 오프를 고민해본 적이 있다.
-- [ ] 반복되는 수정과 배포에 수반되는 작업들을 자동화 해보았다.
-- [ ] 언어나 프레임워크만으로 구현할 수 없는 것들을 직접 구현해보았다.
-- [ ] 내가 사용한 라이브러리나 프레임 워크의 한계를 느끼고 개선해보았다.
-- [ ] 코드나 제품의 퀄리티를 유지하기 위한 분석툴이나 테스트 툴을 도입해보았다.
-- [ ] 타인과의 협업을 효율적으로 하기 위한 고민을 해보았다.
+- [ ] 실제 서비스를 공개적으로 배포하고 운영하는 경험을 해보았다.
+- [ ] 유저의 피드백에 따라 성능/사용성을 개선하고 신규 기능을 추가해보았다.
+- [ ] 발견되는 버그와 개선사항들을 정리하고 쌓인 이슈들을 체계적으로 관리해보았다.
+- [ ] 코드를 지속적으로 리팩토링하고 디자인 패턴을 적용해보았다.
+- [ ] 위의 시도에서 더 좋은 설계와 더 빠른 개발 사이의 트레이드 오프를 고민해본 적이 있다.
+- [ ] 반복되는 수정과 배포에 수반되는 작업들을 자동화 해보았다.
+- [ ] 언어나 프레임워크만으로 구현할 수 없는 것들을 직접 구현해보았다.
+- [ ] 내가 사용한 라이브러리나 프레임 워크의 한계를 느끼고 개선해보았다.
+- [ ] 코드나 제품의 퀄리티를 유지하기 위한 분석툴이나 테스트 툴을 도입해보았다.
+- [X] 타인과의 협업을 효율적으로 하기 위한 고민을 해보았다.
## System Architecture
diff --git a/build.gradle b/build.gradle
index 1ad06af1..f44b5884 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,6 +33,12 @@ dependencies {
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
implementation 'org.apache.httpcomponents:httpcore:4.4.14'
+ // jackson
+ implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.5'
+ implementation 'com.fasterxml.jackson.core:jackson-core:2.13.5'
+ implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.5'
+ implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.5'
+
// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
diff --git a/docker-compose.yml b/docker-compose.yml
index dac34262..c3e698b2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,15 +1,15 @@
version: '3.7'
services:
- proxy:
- image: jaeseo/nginx:latest
- restart: always
- ports:
- - "80:80"
- - "443:443"
- networks:
- - was-net
- depends_on:
- - api
+# proxy:
+# image: jaeseo/nginx:latest
+# restart: always
+# ports:
+# - "80:80"
+# - "443:443"
+# networks:
+# - was-net
+# depends_on:
+# - api
fitapet-api:
image: jaeseo/fitapet:latest
@@ -18,6 +18,8 @@ services:
- "8080:8080"
env_file:
- .env
+ environment:
+ - TZ=Asia/Seoul
networks:
- was-net
diff --git a/src/main/java/com/kcy/fitapet/FitapetApplication.java b/src/main/java/com/kcy/fitapet/FitapetApplication.java
index afbc1e60..2418e60a 100644
--- a/src/main/java/com/kcy/fitapet/FitapetApplication.java
+++ b/src/main/java/com/kcy/fitapet/FitapetApplication.java
@@ -1,12 +1,19 @@
package com.kcy.fitapet;
+import jakarta.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import java.util.TimeZone;
+
@SpringBootApplication
public class FitapetApplication {
public static void main(String[] args) {
SpringApplication.run(FitapetApplication.class, args);
}
+ @PostConstruct
+ public void init() {
+ TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
+ }
}
diff --git a/src/main/java/com/kcy/fitapet/domain/member/api/MemberApi.java b/src/main/java/com/kcy/fitapet/domain/member/api/MemberApi.java
index f98f9310..cfabbb26 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/api/MemberApi.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/api/MemberApi.java
@@ -1,5 +1,6 @@
package com.kcy.fitapet.domain.member.api;
+import com.kcy.fitapet.domain.member.dao.MemberRepository;
import com.kcy.fitapet.domain.member.dto.auth.SignInReq;
import com.kcy.fitapet.domain.member.dto.auth.SignUpReq;
import com.kcy.fitapet.domain.member.dto.sms.SmsReq;
diff --git a/src/main/java/com/kcy/fitapet/domain/member/domain/Manager.java b/src/main/java/com/kcy/fitapet/domain/member/domain/Manager.java
index 1f8302de..f2e90f41 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/domain/Manager.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/domain/Manager.java
@@ -4,6 +4,7 @@
import com.kcy.fitapet.domain.pet.domain.Pet;
import jakarta.persistence.*;
import lombok.AccessLevel;
+import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.ColumnDefault;
import org.springframework.data.annotation.CreatedDate;
@@ -11,6 +12,7 @@
import java.time.LocalDateTime;
@Entity
+@Getter
@Table(name = "MANAGER")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Manager extends Auditable {
diff --git a/src/main/java/com/kcy/fitapet/domain/member/domain/Member.java b/src/main/java/com/kcy/fitapet/domain/member/domain/Member.java
index 7807dc2b..50ed571b 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/domain/Member.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/domain/Member.java
@@ -1,5 +1,7 @@
package com.kcy.fitapet.domain.member.domain;
+import com.kcy.fitapet.domain.member.type.RoleType;
+import com.kcy.fitapet.domain.member.type.RoleTypeConverter;
import com.kcy.fitapet.domain.model.Auditable;
import com.kcy.fitapet.domain.notification.domain.Notification;
import com.kcy.fitapet.domain.notification.domain.NotificationSetting;
@@ -26,13 +28,14 @@ public class Member extends Auditable {
@Getter
private String uid;
+ @Getter
private String name;
private String password;
@Getter
private String phone;
@Getter
private String email;
- @Column(name = "profile_img")
+ @Column(name = "profile_img") @Getter
private String profileImg;
@Column(name = "account_locked") @ColumnDefault("false")
private Boolean accountLocked;
@@ -50,9 +53,9 @@ public class Member extends Auditable {
private List notifications = new ArrayList<>();
@OneToOne(mappedBy = "member", cascade = CascadeType.ALL)
private NotificationSetting notificationSetting;
- @OneToMany(mappedBy = "master")
+ @OneToMany(mappedBy = "master") @Getter
private List pets = new ArrayList<>();
- @OneToMany(mappedBy = "manager", cascade = CascadeType.ALL)
+ @OneToMany(mappedBy = "manager", cascade = CascadeType.ALL) @Getter
private List underCares = new ArrayList<>();
@Builder
diff --git a/src/main/java/com/kcy/fitapet/domain/member/dto/auth/SignUpReq.java b/src/main/java/com/kcy/fitapet/domain/member/dto/auth/SignUpReq.java
index 1b0bd2b9..595050f1 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/dto/auth/SignUpReq.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/dto/auth/SignUpReq.java
@@ -1,7 +1,7 @@
package com.kcy.fitapet.domain.member.dto.auth;
import com.kcy.fitapet.domain.member.domain.Member;
-import com.kcy.fitapet.domain.member.domain.RoleType;
+import com.kcy.fitapet.domain.member.type.RoleType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
diff --git a/src/main/java/com/kcy/fitapet/domain/member/service/module/SmsService.java b/src/main/java/com/kcy/fitapet/domain/member/service/module/SmsService.java
index 8979cd77..df3f2252 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/service/module/SmsService.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/service/module/SmsService.java
@@ -5,8 +5,10 @@
import com.kcy.fitapet.domain.member.dto.sms.SensReq;
import com.kcy.fitapet.domain.member.dto.sms.SensRes;
import com.kcy.fitapet.domain.member.dto.sms.SmsReq;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
@@ -26,6 +28,8 @@
@Service
@Slf4j
public class SmsService {
+ private final ObjectMapper objectMapper;
+
private final String accessKey;
private final String secretKey;
private final String serviceId;
@@ -35,12 +39,14 @@ public SmsService(
@Value("${ncp.api-key}") String accessKey,
@Value("${ncp.secret-key}") String secretKey,
@Value("${ncp.sms.service-key}") String serviceId,
- @Value("${ncp.sms.sender-phone}") String phone
+ @Value("${ncp.sms.sender-phone}") String phone,
+ @Autowired ObjectMapper objectMapper
) {
this.accessKey = accessKey;
this.secretKey = secretKey;
this.serviceId = serviceId;
this.phone = phone;
+ this.objectMapper = objectMapper;
}
public SensRes sendCertificationNumber(SmsReq smsReq, String certificationNumber)
@@ -57,7 +63,6 @@ public SensRes sendCertificationNumber(SmsReq smsReq, String certificationNumber
SensReq request = SensReq.of("SMS", "COMM", "82", phone, createAuthCodeMessage(certificationNumber), messages);
- ObjectMapper objectMapper = new ObjectMapper();
String body = objectMapper.writeValueAsString(request);
HttpEntity httpEntity = new HttpEntity<>(body, headers);
diff --git a/src/main/java/com/kcy/fitapet/domain/member/domain/RoleType.java b/src/main/java/com/kcy/fitapet/domain/member/type/RoleType.java
similarity index 95%
rename from src/main/java/com/kcy/fitapet/domain/member/domain/RoleType.java
rename to src/main/java/com/kcy/fitapet/domain/member/type/RoleType.java
index 8d34376c..c0b63c7d 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/domain/RoleType.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/type/RoleType.java
@@ -1,4 +1,4 @@
-package com.kcy.fitapet.domain.member.domain;
+package com.kcy.fitapet.domain.member.type;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
@@ -20,7 +20,6 @@ public enum RoleType implements LegacyCommonType {
private static final Map stringToEnum =
Stream.of(values()).collect(toMap(Object::toString, e -> e));
-
@JsonValue
public String getRole() { return role; }
@Override
diff --git a/src/main/java/com/kcy/fitapet/domain/member/domain/RoleTypeConverter.java b/src/main/java/com/kcy/fitapet/domain/member/type/RoleTypeConverter.java
similarity index 89%
rename from src/main/java/com/kcy/fitapet/domain/member/domain/RoleTypeConverter.java
rename to src/main/java/com/kcy/fitapet/domain/member/type/RoleTypeConverter.java
index 37903e46..ea0c2955 100644
--- a/src/main/java/com/kcy/fitapet/domain/member/domain/RoleTypeConverter.java
+++ b/src/main/java/com/kcy/fitapet/domain/member/type/RoleTypeConverter.java
@@ -1,4 +1,4 @@
-package com.kcy.fitapet.domain.member.domain;
+package com.kcy.fitapet.domain.member.type;
import com.kcy.fitapet.global.common.util.converter.AbstractLegacyEnumAttributeConverter;
import jakarta.persistence.Converter;
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/api/PetApi.java b/src/main/java/com/kcy/fitapet/domain/pet/api/PetApi.java
new file mode 100644
index 00000000..4ab1d42b
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/api/PetApi.java
@@ -0,0 +1,7 @@
+package com.kcy.fitapet.domain.pet.api;
+
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class PetApi {
+}
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/dao/PetRepository.java b/src/main/java/com/kcy/fitapet/domain/pet/dao/PetRepository.java
new file mode 100644
index 00000000..f28c12cd
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/dao/PetRepository.java
@@ -0,0 +1,7 @@
+package com.kcy.fitapet.domain.pet.dao;
+
+import com.kcy.fitapet.domain.pet.domain.Pet;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface PetRepository extends JpaRepository {
+}
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/domain/Pet.java b/src/main/java/com/kcy/fitapet/domain/pet/domain/Pet.java
index be7b1f12..b2d53bf7 100644
--- a/src/main/java/com/kcy/fitapet/domain/pet/domain/Pet.java
+++ b/src/main/java/com/kcy/fitapet/domain/pet/domain/Pet.java
@@ -5,6 +5,8 @@
import com.kcy.fitapet.domain.member.domain.Member;
import com.kcy.fitapet.domain.memo.domain.Memo;
import com.kcy.fitapet.domain.model.Auditable;
+import com.kcy.fitapet.domain.pet.type.GenderType;
+import com.kcy.fitapet.domain.pet.type.GenderTypeConverter;
import com.kcy.fitapet.domain.schedule.domain.Schedule;
import jakarta.persistence.*;
import lombok.*;
@@ -24,7 +26,8 @@ public class Pet extends Auditable {
private Long id;
@Column(name = "pet_name")
private String petName;
- private boolean gender;
+ @Convert(converter = GenderTypeConverter.class)
+ private GenderType gender;
@Column(name = "pet_profile_img")
private String petProfileImg;
@ColumnDefault("false")
@@ -52,7 +55,7 @@ public class Pet extends Auditable {
private List memos = new ArrayList<>();
@Builder
- private Pet(String petName, boolean gender, String petProfileImg, boolean neutered, LocalDateTime birth,
+ private Pet(String petName, GenderType gender, String petProfileImg, boolean neutered, LocalDateTime birth,
Integer age, String species, String feed) {
this.petName = petName;
this.gender = gender;
@@ -64,7 +67,7 @@ private Pet(String petName, boolean gender, String petProfileImg, boolean neuter
this.feed = feed;
}
- public static Pet of(String petName, boolean gender, String petProfileImg, boolean neutered, LocalDateTime birth,
+ public static Pet of(String petName, GenderType gender, String petProfileImg, boolean neutered, LocalDateTime birth,
Integer age, String species, String feed) {
return Pet.builder()
.petName(petName)
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/dto/PetRegisterReq.java b/src/main/java/com/kcy/fitapet/domain/pet/dto/PetRegisterReq.java
new file mode 100644
index 00000000..4301f2e9
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/dto/PetRegisterReq.java
@@ -0,0 +1,15 @@
+package com.kcy.fitapet.domain.pet.dto;
+
+import com.kcy.fitapet.domain.pet.type.GenderType;
+
+import java.time.LocalDate;
+
+public class PetRegisterReq {
+ private String petName;
+ private String species;
+ private GenderType gender;
+ private boolean neutralization;
+ private LocalDate birthDate;
+
+
+}
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/service/PetManageService.java b/src/main/java/com/kcy/fitapet/domain/pet/service/PetManageService.java
new file mode 100644
index 00000000..a82dcb4c
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/service/PetManageService.java
@@ -0,0 +1,13 @@
+package com.kcy.fitapet.domain.pet.service;
+
+import com.kcy.fitapet.domain.pet.dao.PetRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class PetManageService {
+ private final PetRepository petRepository;
+
+
+}
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/service/PetSearchService.java b/src/main/java/com/kcy/fitapet/domain/pet/service/PetSearchService.java
new file mode 100644
index 00000000..553a8cca
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/service/PetSearchService.java
@@ -0,0 +1,4 @@
+package com.kcy.fitapet.domain.pet.service;
+
+public class PetSearchService {
+}
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/type/GenderType.java b/src/main/java/com/kcy/fitapet/domain/pet/type/GenderType.java
new file mode 100644
index 00000000..0dbf1c2d
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/type/GenderType.java
@@ -0,0 +1,18 @@
+package com.kcy.fitapet.domain.pet.type;
+
+import com.kcy.fitapet.global.common.util.converter.LegacyCommonType;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public enum GenderType implements LegacyCommonType {
+ MALE("1", "수컷"),
+ FEMALE("2", "암컷");
+
+ private final String code;
+ private final String gender;
+
+ @Override
+ public String getCode() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/kcy/fitapet/domain/pet/type/GenderTypeConverter.java b/src/main/java/com/kcy/fitapet/domain/pet/type/GenderTypeConverter.java
new file mode 100644
index 00000000..2088d196
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/domain/pet/type/GenderTypeConverter.java
@@ -0,0 +1,13 @@
+package com.kcy.fitapet.domain.pet.type;
+
+import com.kcy.fitapet.global.common.util.converter.AbstractLegacyEnumAttributeConverter;
+import jakarta.persistence.Converter;
+
+@Converter
+public class GenderTypeConverter extends AbstractLegacyEnumAttributeConverter {
+ private static final String ENUM_NAME = "성별";
+
+ public GenderTypeConverter() {
+ super(GenderType.class, false, ENUM_NAME);
+ }
+}
diff --git a/src/main/java/com/kcy/fitapet/global/common/resolver/access/AccessTokenInfoResolver.java b/src/main/java/com/kcy/fitapet/global/common/resolver/access/AccessTokenInfoResolver.java
index 097f01ef..258cf45a 100644
--- a/src/main/java/com/kcy/fitapet/global/common/resolver/access/AccessTokenInfoResolver.java
+++ b/src/main/java/com/kcy/fitapet/global/common/resolver/access/AccessTokenInfoResolver.java
@@ -1,11 +1,9 @@
package com.kcy.fitapet.global.common.resolver.access;
-import com.kcy.fitapet.global.common.util.cookie.CookieUtil;
import com.kcy.fitapet.global.common.util.jwt.AuthConstants;
import com.kcy.fitapet.global.common.util.jwt.JwtUtil;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorCode;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorException;
-import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
@@ -14,6 +12,7 @@
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
+import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -26,7 +25,6 @@
@Component
public class AccessTokenInfoResolver implements HandlerMethodArgumentResolver {
private final JwtUtil jwtUtil;
- private final CookieUtil cookieUtil;
@Override
public boolean supportsParameter(MethodParameter parameter) {
@@ -34,6 +32,11 @@ public boolean supportsParameter(MethodParameter parameter) {
&& parameter.getParameterType().equals(AccessToken.class);
}
+ /**
+ * 요청받은 액세스 토큰 정보를 추출하여 AccessToken 객체에 담아 반환한다.
+ * 단, 인증 과정에서 재발급이 일어난 경우에는 헤더에 담긴 재발급된 액세스 토큰을 추출한다.
+ * 재발급된 액세스 토큰 추출 시, AccessToken 객체의 isReissued 필드를 true로 설정된다.
+ */
@Override
public Object resolveArgument(
MethodParameter parameter,
diff --git a/src/main/java/com/kcy/fitapet/global/common/response/code/ErrorCode.java b/src/main/java/com/kcy/fitapet/global/common/response/code/ErrorCode.java
index 0fa0d747..beb3d326 100644
--- a/src/main/java/com/kcy/fitapet/global/common/response/code/ErrorCode.java
+++ b/src/main/java/com/kcy/fitapet/global/common/response/code/ErrorCode.java
@@ -61,4 +61,9 @@ public enum ErrorCode implements StatusCode {
private final HttpStatus httpStatus;
private final String message;
+
+ @Override
+ public String getName() {
+ return name();
+ }
}
diff --git a/src/main/java/com/kcy/fitapet/global/common/response/code/StatusCode.java b/src/main/java/com/kcy/fitapet/global/common/response/code/StatusCode.java
index 4d5253ac..de9ad0e5 100644
--- a/src/main/java/com/kcy/fitapet/global/common/response/code/StatusCode.java
+++ b/src/main/java/com/kcy/fitapet/global/common/response/code/StatusCode.java
@@ -5,4 +5,5 @@
public interface StatusCode {
HttpStatus getHttpStatus();
String getMessage();
+ String getName();
}
\ No newline at end of file
diff --git a/src/main/java/com/kcy/fitapet/global/common/response/exception/GlobalErrorException.java b/src/main/java/com/kcy/fitapet/global/common/response/exception/GlobalErrorException.java
index 2c2d736a..db4ea061 100644
--- a/src/main/java/com/kcy/fitapet/global/common/response/exception/GlobalErrorException.java
+++ b/src/main/java/com/kcy/fitapet/global/common/response/exception/GlobalErrorException.java
@@ -1,13 +1,14 @@
package com.kcy.fitapet.global.common.response.exception;
import com.kcy.fitapet.global.common.response.code.ErrorCode;
+import com.kcy.fitapet.global.common.response.code.StatusCode;
import lombok.Getter;
@Getter
public class GlobalErrorException extends RuntimeException {
- private final ErrorCode errorCode;
+ private final StatusCode errorCode;
- public GlobalErrorException(ErrorCode errorCode) {
+ public GlobalErrorException(StatusCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
@@ -15,6 +16,6 @@ public GlobalErrorException(ErrorCode errorCode) {
@Override
public String toString() {
return String.format("GlobalErrorException(code=%s, message=%s)",
- errorCode.name(), errorCode.getMessage());
+ errorCode.getName(), errorCode.getMessage());
}
}
diff --git a/src/main/java/com/kcy/fitapet/global/common/response/handler/GlobalExceptionHandler.java b/src/main/java/com/kcy/fitapet/global/common/response/handler/GlobalExceptionHandler.java
index 04df35d3..3a3a3098 100644
--- a/src/main/java/com/kcy/fitapet/global/common/response/handler/GlobalExceptionHandler.java
+++ b/src/main/java/com/kcy/fitapet/global/common/response/handler/GlobalExceptionHandler.java
@@ -9,12 +9,14 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
@@ -53,11 +55,11 @@ protected ResponseEntity handleAuthErrorException(AuthErrorExcept
* @param e AccessDeniedException
* @return ResponseEntity
*/
+ @ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(AccessDeniedException.class)
- protected ResponseEntity handleAccessDeniedException(AccessDeniedException e) {
+ protected ErrorResponse handleAccessDeniedException(AccessDeniedException e) {
log.warn("handleAccessDeniedException : {}", e.getMessage());
- final ErrorResponse response = ErrorResponse.of(ErrorCode.FORBIDDEN_ERROR.getMessage());
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response);
+ return ErrorResponse.of(ErrorCode.FORBIDDEN_ERROR.getMessage());
}
/**
@@ -90,11 +92,11 @@ protected ResponseEntity handleMissingRequestHeaderException(Mi
* @param e HttpMessageNotReadableException
* @return ResponseEntity
*/
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
- protected ResponseEntity handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
+ protected ErrorResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
log.warn("handleHttpMessageNotReadableException : {}", e.getMessage());
- final ErrorResponse response = ErrorResponse.of(ErrorCode.MISSING_REQUEST_BODY_ERROR.getMessage());
- return ResponseEntity.badRequest().body(response);
+ return ErrorResponse.of(ErrorCode.MISSING_REQUEST_BODY_ERROR.getMessage());
}
/**
@@ -102,11 +104,11 @@ protected ResponseEntity handleHttpMessageNotReadableException(Ht
* @param e MissingServletRequestParameterException
* @return ResponseEntity
*/
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
- protected ResponseEntity handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
+ protected ErrorResponse handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
log.warn("handleMissingServletRequestParameterException : {}", e.getMessage());
- final ErrorResponse response = ErrorResponse.of(ErrorCode.MISSING_REQUEST_PARAMETER_ERROR.getMessage());
- return ResponseEntity.badRequest().body(response);
+ return ErrorResponse.of(ErrorCode.MISSING_REQUEST_PARAMETER_ERROR.getMessage());
}
/**
@@ -114,11 +116,23 @@ protected ResponseEntity handleMissingServletRequestParameterExce
* @param e NoHandlerFoundException
* @return ResponseEntity
*/
+ @ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoHandlerFoundException.class)
- protected ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException e) {
+ protected ErrorResponse handleNoHandlerFoundException(NoHandlerFoundException e) {
log.warn("handleNoHandlerFoundException : {}", e.getMessage());
- final ErrorResponse response = ErrorResponse.of(ErrorCode.NOT_FOUND_ERROR.getMessage());
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
+ return ErrorResponse.of(ErrorCode.NOT_FOUND_ERROR.getMessage());
+ }
+
+ /**
+ * API 호출 시 데이터를 반환할 수 없는 경우
+ * @param e HttpMessageNotWritableException
+ * @return ResponseEntity
+ */
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ @ExceptionHandler(HttpMessageNotWritableException.class)
+ protected ErrorResponse handleHttpMessageNotWritableException(HttpMessageNotWritableException e) {
+ log.warn("handleHttpMessageNotWritableException : {}", e.getMessage());
+ return ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR.getMessage());
}
/**
@@ -126,11 +140,11 @@ protected ResponseEntity handleNoHandlerFoundException(NoHandlerF
* @param e NullPointerException
* @return ResponseEntity
*/
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(NullPointerException.class)
- protected ResponseEntity handleNullPointerException(NullPointerException e) {
+ protected ErrorResponse handleNullPointerException(NullPointerException e) {
log.warn("handleNullPointerException : {}", e.getMessage());
- final ErrorResponse response = ErrorResponse.of(ErrorCode.NULL_POINT_ERROR.getMessage());
- return ResponseEntity.internalServerError().body(response);
+ return ErrorResponse.of(ErrorCode.NULL_POINT_ERROR.getMessage());
}
// ================================================================================== //
@@ -142,7 +156,9 @@ protected ResponseEntity handleNullPointerException(NullPointerEx
*/
@ExceptionHandler(Exception.class)
protected ResponseEntity handleException(Exception e) {
- log.warn("handleException : {}", e.getMessage());
+ log.warn("{} : handleException : {}", e.getClass(), e.getMessage());
+ e.printStackTrace();
+
final ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR.getMessage());
return ResponseEntity.internalServerError().body(response);
}
diff --git a/src/main/java/com/kcy/fitapet/global/common/security/authentication/CustomUserDetails.java b/src/main/java/com/kcy/fitapet/global/common/security/authentication/CustomUserDetails.java
index 42fec892..6bb3e662 100644
--- a/src/main/java/com/kcy/fitapet/global/common/security/authentication/CustomUserDetails.java
+++ b/src/main/java/com/kcy/fitapet/global/common/security/authentication/CustomUserDetails.java
@@ -2,7 +2,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kcy.fitapet.domain.member.domain.Member;
-import com.kcy.fitapet.domain.member.domain.RoleType;
+import com.kcy.fitapet.domain.member.type.RoleType;
import com.kcy.fitapet.global.common.util.jwt.entity.JwtUserInfo;
import lombok.Builder;
import lombok.Getter;
diff --git a/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtAuthenticationFilter.java b/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtAuthenticationFilter.java
index c843b583..9bda3e10 100644
--- a/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtAuthenticationFilter.java
+++ b/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtAuthenticationFilter.java
@@ -46,8 +46,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final CookieUtil cookieUtil;
private final List jwtIgnoreUrls = List.of(
- "/api/v1/members/register",
- "/api/v1/members/login",
+ "/api/v1/test", "/api/v1/test/**",
+ "/api/v1/members/register", "/api/v1/members/login",
"/api/v1/members/refresh",
"/api/v1/members/sms/**",
"/v3/api-docs/**", "/swagger-ui/**", "/swagger",
diff --git a/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtExceptionFilter.java b/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtExceptionFilter.java
index 3970a95d..da167adc 100644
--- a/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtExceptionFilter.java
+++ b/src/main/java/com/kcy/fitapet/global/common/security/filter/JwtExceptionFilter.java
@@ -1,25 +1,23 @@
package com.kcy.fitapet.global.common.security.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.kcy.fitapet.global.common.util.exception.JwtErrorCodeUtil;
-import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorCode;
+import com.kcy.fitapet.global.common.util.jwt.exception.JwtErrorCodeUtil;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorException;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorResponse;
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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
-import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
-
@Slf4j
+@RequiredArgsConstructor
public class JwtExceptionFilter extends OncePerRequestFilter {
- private final ObjectMapper objectMapper = new ObjectMapper();
+ private final ObjectMapper objectMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/converter/LegacyEnumValueConvertUtils.java b/src/main/java/com/kcy/fitapet/global/common/util/converter/LegacyEnumValueConvertUtils.java
index 7f9c910f..d17af103 100644
--- a/src/main/java/com/kcy/fitapet/global/common/util/converter/LegacyEnumValueConvertUtils.java
+++ b/src/main/java/com/kcy/fitapet/global/common/util/converter/LegacyEnumValueConvertUtils.java
@@ -17,7 +17,7 @@ public static & LegacyCommonType> T ofLegacyCode(Class enu
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
- String.format("enum=[%s], code=[%s]가 존재하지 않습니다.", enumClass.getName(), code))); // TODO : 공통 예외로 변경
+ String.format("enum=[%s], code=[%s]가 존재하지 않습니다.", enumClass.getName(), code)));
}
public static & LegacyCommonType> String toLegacyCode(T enumValue) {
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/dtobind/Dto.java b/src/main/java/com/kcy/fitapet/global/common/util/dtobind/Dto.java
new file mode 100644
index 00000000..9908c986
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/global/common/util/dtobind/Dto.java
@@ -0,0 +1,12 @@
+package com.kcy.fitapet.global.common.util.dtobind;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Dto {
+ String name() default "default";
+}
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/dtobind/DtoMapper.java b/src/main/java/com/kcy/fitapet/global/common/util/dtobind/DtoMapper.java
new file mode 100644
index 00000000..00dc1758
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/global/common/util/dtobind/DtoMapper.java
@@ -0,0 +1,115 @@
+package com.kcy.fitapet.global.common.util.dtobind;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.kcy.fitapet.global.common.response.code.ErrorCode;
+import com.kcy.fitapet.global.common.response.exception.GlobalErrorException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * {@link Dto} 어노테이션이 선언된 클래스를 JSend 형식의 Map으로 변환한다.
+ */
+@Slf4j
+@Component
+public final class DtoMapper {
+ private final Map format = new HashMap<>();
+
+ /**
+ * data 객체를 JSend 형식의 Map으로 변환한다.
+ * 이 때, data 객체는 @Dto 어노테이션이 선언되어 있어야 하며, name 속성이 JSON의 key 값으로 사용된다.
+ * 응답 도메인이 여러 개인 경우, 외부 클래스에서 중첩 클래스를 관리하는 필드는 @JsonIgnore 어노테이션을 선언해야 하며,
+ * 최종 응답 형태에서 외부 클래스와 동일한 레벨로 분리된다.
+ * 중첩 클래스는 @InnerDto 어노테이션을 선언해야 하며, name 속성이 JSON의 key 값으로 사용된다.
+ * 중첨 클래스 내에서 직렬화를 원치 않는 필드는 @JsonIgnore 어노테이션을 선언해야 한다. (혹은 getter 메서드를 선언하지 않으면 된다.)
+ * @param data Object : @Dto 타입 객체
+ * @return Map
+ */
+ // TODO(YANG JAESEO) : 2021/10/14 Pageable 객체 처리
+ // Pageable 객체를 JSend 형식의 Map으로 변환할 수 있도록 처리한다. (PageDto를 상속받아서 사용)
+ // labels: enhancement
+ public Map from(T data) {
+ // 1. data의 클래스를 가져온다.
+ Class> type = data.getClass();
+
+ // 2. 외부 및 내부 클래스를 가져온다.
+ // (단, Builder 클래스 및 @Dto가 정의되지 않은 클래스는 제외한다.)
+ List instances = Arrays.stream(type.getNestMembers())
+ .filter(cl -> !cl.getName().endsWith("Builder") || cl.isAnnotationPresent(Dto.class))
+ .map(this::getInstance)// 3. 클래스 생성자를 이용해 인스턴스 생성
+ .toList();
+
+ // 4. data의 값을 도메인 별로 binding하여 Map 형태로 포매팅한다. setter가 없으므로 getter 메서드를 이용한다.
+ // (단, @JsonIgnore가 선언된 필드는 내부 클래스에 포함되지 않으며, 단지 중첩 클래스에 값을 복사하기 위해 사용한다.)
+ instances.forEach(instance -> {
+ if (instance.getClass().isAnnotationPresent(InnerDto.class)) {
+ format.put(instance.getClass().getAnnotation(InnerDto.class).name(), extractInnerDtoValues(data, instance));
+ } else {
+ format.put(instance.getClass().getAnnotation(Dto.class).name(), extractDtoValues(data, instance));
+ }
+ });
+
+ return format;
+ }
+
+ private Object getInstance(Class> cl) {
+ Object instance;
+ try {
+ instance = cl.getDeclaredConstructor().newInstance();
+ } catch (NoSuchMethodException e) {
+ log.error("[ERROR] Dto 클래스에 기본 생성자가 없습니다.");
+ throw new GlobalErrorException(ErrorCode.INTERNAL_SERVER_ERROR);
+ } catch (Exception e) {
+ log.error("[ERROR] Dto 클래스를 생성할 수 없습니다. : {}", e.getMessage());
+ throw new GlobalErrorException(ErrorCode.INTERNAL_SERVER_ERROR);
+ }
+ return instance;
+ }
+
+ private Object extractInnerDtoValues(T data, Object instance) {
+ Class> declaringClass = instance.getClass().getDeclaringClass();
+ String name = instance.getClass().getAnnotation(InnerDto.class).name();
+ String getterName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
+
+ log.info("[INFO] 내부 클래스 {} - 외부 클래스 {}", instance.getClass().getName(), declaringClass.getName());
+ log.info("[INFO] 외부 클래스 메서드 호출 : getterName : {}", getterName);
+
+ try {
+ Method getter = declaringClass.getMethod(getterName);
+ Object value = getter.invoke(data);
+
+ return ObjectUtils.isEmpty(value) ? null : value;
+ } catch (Exception e) {
+ log.error("[ERROR] Dto 클래스의 getter 메서드를 호출할 수 없거나 필드에 값을 할당할 수 없습니다.");
+ throw new GlobalErrorException(ErrorCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ private Map extractDtoValues(T data, Object instance) {
+ Map fields = new HashMap<>();
+ for (Field field : instance.getClass().getDeclaredFields()) {
+ if (field.isAnnotationPresent(JsonIgnore.class)) continue;
+
+ String fieldName = field.getName();
+ String fieldGetterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
+
+ try {
+ Method fieldGetter = instance.getClass().getMethod(fieldGetterName);
+ Object fieldValue = fieldGetter.invoke(data);
+
+ fields.put(fieldName, fieldValue);
+ } catch (Exception e) {
+ log.error("[ERROR] Dto 클래스의 getter 메서드를 호출할 수 없거나 필드에 값을 할당할 수 없습니다.");
+ throw new GlobalErrorException(ErrorCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+ return fields;
+ }
+}
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/dtobind/InnerDto.java b/src/main/java/com/kcy/fitapet/global/common/util/dtobind/InnerDto.java
new file mode 100644
index 00000000..586f4049
--- /dev/null
+++ b/src/main/java/com/kcy/fitapet/global/common/util/dtobind/InnerDto.java
@@ -0,0 +1,13 @@
+package com.kcy.fitapet.global.common.util.dtobind;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Dto
+public @interface InnerDto {
+ String name() default "default";
+}
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/jwt/JwtUtilImpl.java b/src/main/java/com/kcy/fitapet/global/common/util/jwt/JwtUtilImpl.java
index ec491ea4..f9b2bf9c 100644
--- a/src/main/java/com/kcy/fitapet/global/common/util/jwt/JwtUtilImpl.java
+++ b/src/main/java/com/kcy/fitapet/global/common/util/jwt/JwtUtilImpl.java
@@ -1,8 +1,8 @@
package com.kcy.fitapet.global.common.util.jwt;
-import com.kcy.fitapet.domain.member.domain.RoleType;
+import com.kcy.fitapet.domain.member.type.RoleType;
import com.kcy.fitapet.global.common.util.DateUtil;
-import com.kcy.fitapet.global.common.util.exception.JwtErrorCodeUtil;
+import com.kcy.fitapet.global.common.util.jwt.exception.JwtErrorCodeUtil;
import com.kcy.fitapet.global.common.util.jwt.entity.JwtUserInfo;
import com.kcy.fitapet.global.common.util.jwt.entity.SmsAuthInfo;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorCode;
@@ -11,12 +11,12 @@
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
-import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.time.Duration;
import java.time.LocalDateTime;
@@ -24,7 +24,6 @@
import java.util.Date;
import java.util.Map;
-// TODO : JwtUtilImpl의 모든 예외 발생 제거
/**
* JWT 토큰 생성 및 검증을 담당하는 클래스
*/
@@ -35,7 +34,9 @@ public class JwtUtilImpl implements JwtUtil {
private static final String ROLE = "role";
private static final String PHONE_NUMBER = "phoneNumber";
- private final String jwtSecretKey;
+ private final Key signatureKey;
+ private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
private final Duration accessTokenExpirationTime;
private final Duration refreshTokenExpirationTime;
private final Duration smsAuthExpirationTime;
@@ -46,14 +47,16 @@ public JwtUtilImpl(
@Value("${jwt.token.refresh-expiration-time}") Duration refreshTokenExpirationTime,
@Value("${jwt.token.sms-auth-expiration-time}") Duration smsAuthExpirationTime
) {
- this.jwtSecretKey = jwtSecretKey;
+ final byte[] secretKeyBytes = Base64.getDecoder().decode(jwtSecretKey);
+ this.signatureKey = Keys.hmacShaKeyFor(secretKeyBytes);
+
this.accessTokenExpirationTime = accessTokenExpirationTime;
this.refreshTokenExpirationTime = refreshTokenExpirationTime;
this.smsAuthExpirationTime = smsAuthExpirationTime;
}
@Override
- public String resolveToken(String authHeader) throws AuthErrorException {
+ public String resolveToken(String authHeader) {
if (StringUtils.hasText(authHeader) && authHeader.startsWith(AuthConstants.TOKEN_TYPE.getValue())) {
return authHeader.substring(AuthConstants.TOKEN_TYPE.getValue().length());
}
@@ -61,40 +64,37 @@ public String resolveToken(String authHeader) throws AuthErrorException {
}
@Override
- @SuppressWarnings("deprecation")
public String generateAccessToken(JwtUserInfo user) {
final Date now = new Date();
return Jwts.builder()
.setHeader(createHeader())
.setClaims(createClaims(user))
- .signWith(SignatureAlgorithm.HS256, createSignature())
+ .signWith(signatureKey, signatureAlgorithm)
.setExpiration(createExpireDate(now, accessTokenExpirationTime.toMillis()))
.compact();
}
@Override
- @SuppressWarnings("deprecation")
public String generateRefreshToken(JwtUserInfo user) {
final Date now = new Date();
return Jwts.builder()
.setHeader(createHeader())
.setClaims(createClaims(user))
- .signWith(SignatureAlgorithm.HS256, createSignature())
+ .signWith(signatureKey, signatureAlgorithm)
.setExpiration(createExpireDate(now, refreshTokenExpirationTime.toMillis()))
.compact();
}
@Override
- @SuppressWarnings("deprecation")
public String generateSmsAuthToken(SmsAuthInfo user) {
final Date now = new Date();
return Jwts.builder()
.setHeader(createHeader())
.setClaims(Map.of(PHONE_NUMBER, user.phoneNumber()))
- .signWith(SignatureAlgorithm.HS256, createSignature())
+ .signWith(signatureKey, signatureAlgorithm)
.setExpiration(createExpireDate(now, smsAuthExpirationTime.toMillis()))
.compact();
}
@@ -158,19 +158,13 @@ private Map createClaims(JwtUserInfo dto) {
ROLE, dto.role().getRole());
}
- private Key createSignature() {
- byte[] secretKeyBytes = Base64.getDecoder().decode(jwtSecretKey);
- return new SecretKeySpec(secretKeyBytes, SignatureAlgorithm.HS256.getJcaName());
- }
-
private Date createExpireDate(final Date now, long expirationTime) {
return new Date(now.getTime() + expirationTime);
}
- @SuppressWarnings("deprecation")
private Claims getClaimsFromToken(String token) {
- return Jwts.parser()
- .setSigningKey(Base64.getDecoder().decode(jwtSecretKey))
+ return Jwts.parserBuilder()
+ .setSigningKey(signatureKey).build()
.parseClaimsJws(token)
.getBody();
}
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/jwt/entity/JwtUserInfo.java b/src/main/java/com/kcy/fitapet/global/common/util/jwt/entity/JwtUserInfo.java
index 6ffd8d94..173dbab1 100644
--- a/src/main/java/com/kcy/fitapet/global/common/util/jwt/entity/JwtUserInfo.java
+++ b/src/main/java/com/kcy/fitapet/global/common/util/jwt/entity/JwtUserInfo.java
@@ -1,9 +1,8 @@
package com.kcy.fitapet.global.common.util.jwt.entity;
import com.kcy.fitapet.domain.member.domain.Member;
-import com.kcy.fitapet.domain.member.domain.RoleType;
+import com.kcy.fitapet.domain.member.type.RoleType;
import lombok.Builder;
-import lombok.ToString;
@Builder
public record JwtUserInfo (
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/AuthErrorCode.java b/src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/AuthErrorCode.java
index e48744fa..d85b3ad4 100644
--- a/src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/AuthErrorCode.java
+++ b/src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/AuthErrorCode.java
@@ -43,4 +43,9 @@ public enum AuthErrorCode implements StatusCode {
private final HttpStatus httpStatus;
private final String message;
+
+ @Override
+ public String getName() {
+ return name();
+ }
}
diff --git a/src/main/java/com/kcy/fitapet/global/common/util/exception/JwtErrorCodeUtil.java b/src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/JwtErrorCodeUtil.java
similarity index 98%
rename from src/main/java/com/kcy/fitapet/global/common/util/exception/JwtErrorCodeUtil.java
rename to src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/JwtErrorCodeUtil.java
index 96caba02..5c99fedd 100644
--- a/src/main/java/com/kcy/fitapet/global/common/util/exception/JwtErrorCodeUtil.java
+++ b/src/main/java/com/kcy/fitapet/global/common/util/jwt/exception/JwtErrorCodeUtil.java
@@ -1,4 +1,4 @@
-package com.kcy.fitapet.global.common.util.exception;
+package com.kcy.fitapet.global.common.util.jwt.exception;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorCode;
import com.kcy.fitapet.global.common.util.jwt.exception.AuthErrorException;
diff --git a/src/main/java/com/kcy/fitapet/global/config/SwaggerConfig.java b/src/main/java/com/kcy/fitapet/global/config/SwaggerConfig.java
index 8e7cf3db..e0b5aee5 100644
--- a/src/main/java/com/kcy/fitapet/global/config/SwaggerConfig.java
+++ b/src/main/java/com/kcy/fitapet/global/config/SwaggerConfig.java
@@ -22,7 +22,7 @@
)
@RequiredArgsConstructor
public class SwaggerConfig {
- private static String JWT = "jwtAuth";
+ private static final String JWT = "jwtAuth";
private final Environment environment;
@Bean
diff --git a/src/main/java/com/kcy/fitapet/global/config/security/SecurityConfig.java b/src/main/java/com/kcy/fitapet/global/config/security/SecurityConfig.java
index 80762101..3ca4e1d1 100644
--- a/src/main/java/com/kcy/fitapet/global/config/security/SecurityConfig.java
+++ b/src/main/java/com/kcy/fitapet/global/config/security/SecurityConfig.java
@@ -30,6 +30,7 @@ public class SecurityConfig {
private final JwtSecurityConfig jwtSecurityConfig;
private final String[] publicEndpoints = {
+ "/api/v1/test", "/api/v1/test/**",
"/favicon.ico",
// Swagger
diff --git a/src/main/java/com/kcy/fitapet/global/config/security/SecurityFilterConfig.java b/src/main/java/com/kcy/fitapet/global/config/security/SecurityFilterConfig.java
index 26dbce95..c00d2852 100644
--- a/src/main/java/com/kcy/fitapet/global/config/security/SecurityFilterConfig.java
+++ b/src/main/java/com/kcy/fitapet/global/config/security/SecurityFilterConfig.java
@@ -1,5 +1,6 @@
package com.kcy.fitapet.global.config.security;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.kcy.fitapet.global.common.security.authentication.UserDetailServiceImpl;
import com.kcy.fitapet.global.common.security.filter.JwtAuthenticationFilter;
import com.kcy.fitapet.global.common.security.filter.JwtExceptionFilter;
@@ -22,9 +23,11 @@ public class SecurityFilterConfig {
private final JwtUtil jwtUtil;
private final CookieUtil cookieUtil;
+ private final ObjectMapper objectMapper;
+
@Bean
public JwtExceptionFilter jwtExceptionFilter() {
- return new JwtExceptionFilter();
+ return new JwtExceptionFilter(objectMapper);
}
@Bean
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 84029370..692d98eb 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -48,7 +48,7 @@ jwt:
secret: ${JWT_SECRET}
token:
# milliseconds 단위
- access-expiration-time: 2000 # 30m (30 * 60 * 1000)
+ access-expiration-time: 1800000 # 30m (30 * 60 * 1000)
refresh-expiration-time: 604800000 # 7d (7 * 24 * 60 * 60 * 1000)
sms-auth-expiration-time: 180000 # 3m (3 * 60 * 1000)
@@ -56,6 +56,7 @@ logging:
level:
org.hibernate.sql: debug
org.hibernate.type: trace
+ com.zaxxer.hikari.HikariConfig: DEBUG
ncp:
api-key: ${NCP_API_ACCESS_KEY}