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}