diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index b6fb8ef..f0b4dc2 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -38,7 +38,6 @@ jobs: - name: Build Project with Gradle run: | - echo ${{ secrets.APPLICATION_SECRET }} | base64 --decode > ./src/main/resources/application-secret.yml ./gradlew bootJar - name: Login to DockerHub @@ -48,51 +47,71 @@ jobs: password: ${{ secrets.DOCKER_TOKEN }} - name: Build docker image - run: docker build --platform linux/amd64 -t ${{ secrets.DOCKER_USERNAME }}/qpin_server . + run: docker build --platform linux/amd64 --build-arg PROFILES=prod --build-arg ENV=prod -t ${{ secrets.DOCKER_USERNAME }}/qpin_server . - name: Publish image to docker hub run: docker push ${{ secrets.DOCKER_USERNAME }}/qpin_server:latest -# CD 임시 삭제 -# deploy: -# needs: build -# runs-on: ubuntu-latest -# steps: -# - name: Set Target IP -# run: | -# STATUS=$(curl -o /dev/null -w "%{http_code}" "http://${{ secrets.QPIN_SERVER_IP }}/env") -# echo $STATUS -# if [ $STATUS = 200 ]; then -# CURRENT_UPSTREAM=$(curl -s "http://${{ secrets.QPIN_SERVER_IP }}/env") -# else -# CURRENT_UPSTREAM=green -# fi -# echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV -# if [ $CURRENT_UPSTREAM = blue ]; then -# echo "CURRENT_PORT=8080" >> $GITHUB_ENV -# echo "STOPPED_PORT=8081" >> $GITHUB_ENV -# echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV -# else -# echo "CURRENT_PORT=8081" >> $GITHUB_ENV -# echo "STOPPED_PORT=8080" >> $GITHUB_ENV -# echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV -# fi -# -# - name: Docker Compose -# uses: appleboy/ssh-action@master -# with: -# username: ubuntu -# host: ${{ secrets.QPIN_SERVER_IP }} -# key: ${{ secrets.EC2_SSH_KEY }} -# script_stop: true -# script: | -# sudo docker pull ${{ secrets.DOCKER_USERNAME }}/qpin_server:latest -# sudo docker-compose -f docker-compose-${{env.TARGET_UPSTREAM}}.yml up -d -# + deploy: + needs: build + runs-on: ubuntu-latest + steps: + - name: Set Target IP + run: | + STATUS=$(curl -o /dev/null -w "%{http_code}" "http://${{ secrets.QPIN_SERVER_IP }}/env") + echo $STATUS + + if [ $STATUS = 200 ]; then + CURRENT_UPSTREAM=$(curl -s "http://${{ secrets.QPIN_SERVER_IP }}/env") + else + CURRENT_UPSTREAM=green + fi + + echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV + if [ $CURRENT_UPSTREAM = blue ]; then + echo "CURRENT_PORT=8080" >> $GITHUB_ENV + echo "STOPPED_PORT=8081" >> $GITHUB_ENV + echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV + else + echo "CURRENT_PORT=8081" >> $GITHUB_ENV + echo "STOPPED_PORT=8080" >> $GITHUB_ENV + echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV + fi + + - name: Generate .env file + uses: appleboy/ssh-action@master + with: + username: ubuntu + host: ${{ secrets.QPIN_SERVER_IP }} + key: ${{ secrets.EC2_SSH_KEY }} + script_stop: true + script: | + ENV_PATH="/home/ubuntu/.env" + echo "ENV=prod" > $ENV_PATH + echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> $ENV_PATH + echo "DB_URL=${{ secrets.DB_URL }}" >> $ENV_PATH + echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> $ENV_PATH + echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> $ENV_PATH + echo "QPIN_SERVER_IP=${{ secrets.QPIN_SERVER_IP }}" >> $ENV_PATH + echo "PUBLIC_API_KEY=${{ secrets.PUBLIC_API_KEY }}" >> $ENV_PATH + + - name: Docker Compose + uses: appleboy/ssh-action@master + with: + username: ubuntu + host: ${{ secrets.QPIN_SERVER_IP }} + key: ${{ secrets.EC2_SSH_KEY }} + script_stop: true + script: | + sudo docker pull ${{ secrets.DOCKER_USERNAME }}/qpin_server:latest + cd /home/ubuntu + sudo docker-compose -f docker-compose-${{env.TARGET_UPSTREAM}}.yml up -d + +# 추후 수정할 예정 - 무중단배포 # - name: Check deploy server URL # uses: jtalk/url-health-check-action@v3 # with: -# url: http://${{ secrets.QPIN_SERVER_IP }}:${{ env.STOPPED_PORT }}/env +# url: http://${{ secrets.QPIN_SERVER_IP }}:${{ secrets.STOPPED_PORT }}/env # max-attempts: 5 # retry-delay: 10s # @@ -104,7 +123,7 @@ jobs: # key: ${{ secrets.EC2_SSH_KEY }} # script_stop: true # script: | -# sudo docker exec -i nginxserver bash -c 'echo "set \$service_url ${{env.TARGET_UPSTREAM}};" > etc/nginx/conf.d/service-env.inc && nginx -s reload' +# sudo docker exec -i qpin-server bash -c 'echo "set \$service_url ${{ env.TARGET_UPSTREAM }};" > /etc/nginx/conf.d/service-env.inc && nginx -s reload' # # - name: Stop current server # uses: appleboy/ssh-action@master diff --git a/Dockerfile b/Dockerfile index 919a66c..ac6f818 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,12 +2,19 @@ FROM openjdk:17-jdk-slim # 빌드 인수 정의 -ARG JAR_FILE=/build/libs/*.jar ARG PROFILES ARG ENV # JAR 파일 복사 +ARG JAR_FILE=build/libs/*.jar COPY ${JAR_FILE} app.jar +# 실행 시점에 환경변수로 전달받은 값 적용 (기본값 지정 가능) +ENV SPRING_PROFILES_ACTIVE=${PROFILES} +ENV SERVER_ENV=${ENV} + +# JVM의 기본 파일 인코딩을 UTF-8로 강제 설정 +ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" + # 컨테이너 시작 명령 설정 -ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILES}", "-Dserver.env=${ENV}", "-jar", "app.jar"] \ No newline at end of file +ENTRYPOINT ["sh", "-c", "java -Dspring.profiles.active=$SPRING_PROFILES_ACTIVE -Dserver.env=$SERVER_ENV -jar app.jar"] \ No newline at end of file diff --git a/build.gradle b/build.gradle index c1c78a8..621538b 100644 --- a/build.gradle +++ b/build.gradle @@ -30,8 +30,8 @@ dependencies { // jwt implementation 'io.jsonwebtoken:jjwt-api:0.12.3' - implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' - implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/org/example/qpin/domain/carphoto/controller/CarPhotoController.java b/src/main/java/org/example/qpin/domain/carphoto/controller/CarPhotoController.java index 86dafdb..9d098bf 100644 --- a/src/main/java/org/example/qpin/domain/carphoto/controller/CarPhotoController.java +++ b/src/main/java/org/example/qpin/domain/carphoto/controller/CarPhotoController.java @@ -1,16 +1,14 @@ package org.example.qpin.domain.carphoto.controller; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.example.qpin.domain.carphoto.dto.CarPhotoRequestDto; -import org.example.qpin.domain.carphoto.dto.CarPhotoResponseDto; +import org.example.qpin.domain.carphoto.dto.*; import org.example.qpin.domain.carphoto.service.CarPhotoService; import org.example.qpin.global.common.response.CommonResponse; import org.example.qpin.global.common.response.ResponseCode; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RestController @RequiredArgsConstructor @@ -20,30 +18,32 @@ public class CarPhotoController { // [Post] 차량 사진 저장 @PostMapping - public CommonResponse> saveCarPhoto(@RequestBody CarPhotoRequestDto carPhotoRequestDto) { - Long newPhoto = carPhotoService.saveCarPhoto(carPhotoRequestDto); + public CommonResponse savePhoto(@RequestBody SavePhotoReqDto reqDto) { + Long newPhoto = carPhotoService.saveCarPhoto(reqDto); - Map result = new HashMap<>(); - result.put("parkingId", String.valueOf(newPhoto)); - result.put("message", "차량 사진 저장이 완료되었습니다"); + SavePhotoResDto response = SavePhotoResDto.builder() + .carPhotoId(newPhoto) + .message("차량 사진 저장이 완료되었습니다.") + .build(); - return new CommonResponse<>(ResponseCode.SUCCESS, result); + return new CommonResponse<>(ResponseCode.SUCCESS, response); } @GetMapping - public CommonResponse> getCarPhotoList(@RequestParam Long memberId) { - List carPhotoList = carPhotoService.getCarPhotoList(memberId); + public CommonResponse> getCarPhotoList(@Valid @RequestBody GetPhotoReqDto reqDto) { + List carPhotoList = carPhotoService.getCarPhotoList(reqDto.getMemberId()); return new CommonResponse<>(ResponseCode.SUCCESS, carPhotoList); } @DeleteMapping("/{photoId}") - public CommonResponse> deleteCarPhoto(@PathVariable Long photoId) { - Long deletePhoto = carPhotoService.deleteCarPhoto(photoId); + public CommonResponse deleteCarPhoto(@Valid @RequestBody DeletePhotoReqDto reqDto) { + Long deletePhoto = carPhotoService.deleteCarPhoto(reqDto.getPhotoId()); - Map result = new HashMap<>(); - result.put("parkingId", String.valueOf(deletePhoto)); - result.put("message", "차량 사진 삭제가 완료되었습니다"); + DeletePhotoResDto response = DeletePhotoResDto.builder() + .photoId(deletePhoto) + .message("차량 사진 삭제가 완료되었습니다.") + .build(); - return new CommonResponse<>(ResponseCode.SUCCESS, result); + return new CommonResponse<>(ResponseCode.SUCCESS, response); } } diff --git a/src/main/java/org/example/qpin/domain/carphoto/dto/DeletePhotoReqDto.java b/src/main/java/org/example/qpin/domain/carphoto/dto/DeletePhotoReqDto.java new file mode 100644 index 0000000..c37c558 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/carphoto/dto/DeletePhotoReqDto.java @@ -0,0 +1,16 @@ +package org.example.qpin.domain.carphoto.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class DeletePhotoReqDto { + @NotNull(message = "photoId는 필수입니다.") + @Positive(message = "photoId는 양수여야 합니다.") + private Long photoId; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/carphoto/dto/CarPhotoRequestDto.java b/src/main/java/org/example/qpin/domain/carphoto/dto/DeletePhotoResDto.java similarity index 63% rename from src/main/java/org/example/qpin/domain/carphoto/dto/CarPhotoRequestDto.java rename to src/main/java/org/example/qpin/domain/carphoto/dto/DeletePhotoResDto.java index 1d9a6f2..99cf270 100644 --- a/src/main/java/org/example/qpin/domain/carphoto/dto/CarPhotoRequestDto.java +++ b/src/main/java/org/example/qpin/domain/carphoto/dto/DeletePhotoResDto.java @@ -6,11 +6,10 @@ import lombok.NoArgsConstructor; @Getter -@Builder @NoArgsConstructor @AllArgsConstructor -public class CarPhotoRequestDto { - private Long userId; - private String carPhotoUrl; - private String parkingArea; -} +@Builder +public class DeletePhotoResDto { + private Long photoId; + private String message; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/carphoto/dto/GetPhotoReqDto.java b/src/main/java/org/example/qpin/domain/carphoto/dto/GetPhotoReqDto.java new file mode 100644 index 0000000..9b73699 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/carphoto/dto/GetPhotoReqDto.java @@ -0,0 +1,16 @@ +package org.example.qpin.domain.carphoto.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class GetPhotoReqDto { + @NotNull(message = "memberId는 필수입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/carphoto/dto/CarPhotoResponseDto.java b/src/main/java/org/example/qpin/domain/carphoto/dto/GetPhotoResDto.java similarity index 66% rename from src/main/java/org/example/qpin/domain/carphoto/dto/CarPhotoResponseDto.java rename to src/main/java/org/example/qpin/domain/carphoto/dto/GetPhotoResDto.java index cf2fdea..72ab3ed 100644 --- a/src/main/java/org/example/qpin/domain/carphoto/dto/CarPhotoResponseDto.java +++ b/src/main/java/org/example/qpin/domain/carphoto/dto/GetPhotoResDto.java @@ -5,12 +5,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; -@Builder @Getter @NoArgsConstructor @AllArgsConstructor -public class CarPhotoResponseDto { +@Builder +public class GetPhotoResDto { private Long carPhotoId; - private String carPhotoUrl; - private String parkingArea; -} + private String carPhotoUrl; // 사진 URL 또는 경로 + private Long parkingAreaId; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/carphoto/dto/SavePhotoReqDto.java b/src/main/java/org/example/qpin/domain/carphoto/dto/SavePhotoReqDto.java new file mode 100644 index 0000000..de9517c --- /dev/null +++ b/src/main/java/org/example/qpin/domain/carphoto/dto/SavePhotoReqDto.java @@ -0,0 +1,25 @@ +package org.example.qpin.domain.carphoto.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.NotBlank; +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SavePhotoReqDto { + + @NotNull(message = "userId는 필수 입력값입니다.") + @Positive(message = "userId는 양수여야 합니다.") + private Long userId; + + @NotNull(message = "parkingAreaId는 필수 입력값입니다.") + @Positive(message = "parkingAreaId는 양수여야 합니다.") + private Long parkingAreaId; + + @NotBlank(message = "carPhotoUrl은 필수 입력값입니다.") + private String carPhotoUrl; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/carphoto/dto/SavePhotoResDto.java b/src/main/java/org/example/qpin/domain/carphoto/dto/SavePhotoResDto.java new file mode 100644 index 0000000..d3100da --- /dev/null +++ b/src/main/java/org/example/qpin/domain/carphoto/dto/SavePhotoResDto.java @@ -0,0 +1,12 @@ +package org.example.qpin.domain.carphoto.dto; + +import lombok.*; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SavePhotoResDto { + private Long carPhotoId; + private String message; +} diff --git a/src/main/java/org/example/qpin/domain/carphoto/entity/CarPhoto.java b/src/main/java/org/example/qpin/domain/carphoto/entity/CarPhoto.java index 7f67698..b2df206 100644 --- a/src/main/java/org/example/qpin/domain/carphoto/entity/CarPhoto.java +++ b/src/main/java/org/example/qpin/domain/carphoto/entity/CarPhoto.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.*; import org.example.qpin.domain.member.entity.Member; +import org.example.qpin.domain.parking.entity.Parking; import org.example.qpin.global.common.BaseEntity; @Entity @@ -14,13 +15,14 @@ public class CarPhoto extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long carPhotoId; + private Long photoId; @Column(nullable = false) - private String carPhotoUrl; + private String photoUrl; - @Column(length = 50, nullable = false) - private String parkingArea; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parking_area_id") + private Parking parking; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") diff --git a/src/main/java/org/example/qpin/domain/carphoto/service/CarPhotoService.java b/src/main/java/org/example/qpin/domain/carphoto/service/CarPhotoService.java index ecff603..7d1f809 100644 --- a/src/main/java/org/example/qpin/domain/carphoto/service/CarPhotoService.java +++ b/src/main/java/org/example/qpin/domain/carphoto/service/CarPhotoService.java @@ -1,12 +1,14 @@ package org.example.qpin.domain.carphoto.service; import lombok.RequiredArgsConstructor; -import org.example.qpin.domain.carphoto.dto.CarPhotoRequestDto; -import org.example.qpin.domain.carphoto.dto.CarPhotoResponseDto; +import org.example.qpin.domain.carphoto.dto.GetPhotoResDto; +import org.example.qpin.domain.carphoto.dto.SavePhotoReqDto; import org.example.qpin.domain.carphoto.entity.CarPhoto; import org.example.qpin.domain.member.entity.Member; +import org.example.qpin.domain.parking.entity.Parking; import org.example.qpin.global.common.repository.CarPhotoRepository; import org.example.qpin.global.common.repository.MemberRepository; +import org.example.qpin.global.common.repository.ParkingRepository; import org.example.qpin.global.exception.BadRequestException; import org.example.qpin.global.exception.ExceptionCode; import org.springframework.stereotype.Service; @@ -19,48 +21,54 @@ public class CarPhotoService { private final CarPhotoRepository carPhotoRepository; + private final ParkingRepository parkingRepository; private final MemberRepository memberRepository; public Member findMemberById(Long memberId) { return memberRepository.findById(memberId).orElseThrow(() -> new BadRequestException(ExceptionCode.NOT_FOUND_MEMBER_ID)); } + public Parking findParkingById(Long parkingId) { + return parkingRepository.findByParkingAreaId(parkingId).orElseThrow(() -> new BadRequestException(ExceptionCode.NOT_FOUND_PARKING)); + } + public CarPhoto findCarPhotoById(Long photoId) { return carPhotoRepository.findById(photoId).orElseThrow(() -> new BadRequestException(ExceptionCode.NOT_FOUND_PHOTO)); } // 사진 저장 - public Long saveCarPhoto(CarPhotoRequestDto carPhotoRequestDto) { + public Long saveCarPhoto(SavePhotoReqDto carPhotoRequestDto) { Member member = findMemberById(carPhotoRequestDto.getUserId()); + Parking parking = findParkingById(carPhotoRequestDto.getParkingAreaId()); CarPhoto newCarPhoto = CarPhoto.builder() - .carPhotoUrl(carPhotoRequestDto.getCarPhotoUrl()) - .parkingArea(carPhotoRequestDto.getParkingArea()) + .photoUrl(carPhotoRequestDto.getCarPhotoUrl()) + .parking(parking) .member(member) .build(); carPhotoRepository.save(newCarPhoto); - return newCarPhoto.getCarPhotoId(); + return newCarPhoto.getPhotoId(); } // 모든 사진 조회 - public List getCarPhotoList(Long memberId) { + public List getCarPhotoList(Long memberId) { findMemberById(memberId); List carPhotos = carPhotoRepository.findByMember_MemberId(memberId); return carPhotos.stream() - .map(carPhoto -> CarPhotoResponseDto.builder() - .carPhotoId(carPhoto.getCarPhotoId()) - .carPhotoUrl(carPhoto.getCarPhotoUrl()) - .parkingArea(carPhoto.getParkingArea()) - .build()) + .map(carPhoto -> GetPhotoResDto.builder() + .carPhotoId(carPhoto.getPhotoId()) + .carPhotoUrl(carPhoto.getPhotoUrl()) + .parkingAreaId(carPhoto.getParking().getParkingAreaId()) + .build()) .collect(Collectors.toList()); } // 사진 삭제 public Long deleteCarPhoto(Long photoId) { CarPhoto deleteCarPhoto = findCarPhotoById(photoId); - carPhotoRepository.deleteById(photoId); - return deleteCarPhoto.getCarPhotoId(); + carPhotoRepository.deleteById(deleteCarPhoto.getPhotoId()); + return deleteCarPhoto.getPhotoId(); } } \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/controller/ParkingController.java b/src/main/java/org/example/qpin/domain/parking/controller/ParkingController.java index bee91ec..392b96c 100644 --- a/src/main/java/org/example/qpin/domain/parking/controller/ParkingController.java +++ b/src/main/java/org/example/qpin/domain/parking/controller/ParkingController.java @@ -1,66 +1,66 @@ package org.example.qpin.domain.parking.controller; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.example.qpin.domain.parking.dto.ParkingInfoResDto; -import org.example.qpin.domain.parking.dto.ParkingSearchReqDto; -import org.example.qpin.domain.parking.dto.ParkingSearchResDto; +import org.example.qpin.domain.parking.dto.*; import org.example.qpin.domain.parking.service.ParkingService; import org.example.qpin.global.common.response.CommonResponse; import org.example.qpin.global.common.response.ResponseCode; import org.json.simple.parser.ParseException; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RestController @RequiredArgsConstructor +@RequestMapping("/parking") public class ParkingController { private final ParkingService parkingService; // [Get] 주변 주차장 검색 - @GetMapping("/parking/selectList") - public CommonResponse> findParkingNearby(@RequestParam ParkingSearchReqDto parkingSearchReqDto) throws ParseException { + @GetMapping("/selectList") + public CommonResponse> findParkingNearby(@Valid ParkingSearchReqDto parkingSearchReqDto) throws ParseException { - Double latitude=parkingSearchReqDto.getLatitude(); - Double longitude=parkingSearchReqDto.getLongitude(); - Double distance=parkingSearchReqDto.getDistance(); - String regionCode=parkingSearchReqDto.getRegionCode(); + List parkingList = parkingService.findParkingNearby( + parkingSearchReqDto.getLatitude(), + parkingSearchReqDto.getLongitude(), + parkingSearchReqDto.getDistance(), + parkingSearchReqDto.getRegionCode() + ); - List parkingList = parkingService.findParkingNearby(latitude, longitude, distance, regionCode); return new CommonResponse<>(ResponseCode.SUCCESS, parkingList); } // [Post] 주차 등록 - @PostMapping("/parking/{parkingAreaId}/{memberId}") - public CommonResponse> parking(@PathVariable("memberId") Long memberId, @PathVariable("parkingAreaId") Long parkingAreaId, @RequestBody String type) { - Long postParkingId = parkingService.postParking(memberId, parkingAreaId, type); + @PostMapping + public CommonResponse parking(@Valid @RequestBody ParkingRegisterReqDto requestDto) { + Long postParkingId = parkingService.postParking(requestDto.getMemberId(), requestDto.getParkingAreaId(), requestDto.getType()); - Map result = new HashMap<>(); - result.put("parkingId", String.valueOf(postParkingId)); - result.put("message", "주차 등록이 완료되었습니다."); + ParkingActionResDto response = ParkingActionResDto.builder() + .parkingId(postParkingId) + .message("주차 등록이 완료되었습니다.") + .build(); - return new CommonResponse<>(ResponseCode.SUCCESS, result); + return new CommonResponse<>(ResponseCode.SUCCESS, response); } // [Delete] 주차 해제 - @DeleteMapping("/parking/{parkingAreaId}/{memberId}") - @ResponseBody - public CommonResponse> deleteParking(@PathVariable("memberId") Long memberId, @PathVariable("parkingAreaId") Long parkingAreaId) { - Long deleteParkingId = parkingService.deleteParking(memberId, parkingAreaId); + @DeleteMapping + public CommonResponse deleteParking(@Valid @RequestBody ParkingReleaseReqDto requestDto) { + Long deleteParkingId = parkingService.deleteParking(requestDto.getMemberId(), requestDto.getParkingAreaId()); - Map result = new HashMap<>(); - result.put("parkingId", String.valueOf(deleteParkingId)); - result.put("message", "주차 해제가 완료되었습니다."); + ParkingActionResDto response = ParkingActionResDto.builder() + .parkingId(deleteParkingId) + .message("주차 해제가 완료되었습니다.") + .build(); - return new CommonResponse<>(ResponseCode.SUCCESS, result); + return new CommonResponse<>(ResponseCode.SUCCESS, response); } // [Get] 현재 주차한 주차장 정보 - @GetMapping("/parking/select/{memberId}") - public CommonResponse parkingInfo(@PathVariable("memberId") Long memberId) { - return new CommonResponse<>(ResponseCode.SUCCESS, parkingService.getParkingInfo(memberId)); + @GetMapping("/select") + public CommonResponse parkingInfo(@Valid @RequestBody ParkingInfoReqDto requestDto) { + return new CommonResponse<>(ResponseCode.SUCCESS, parkingService.getParkingInfo(requestDto.getMemberId())); } } \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/dto/ParkingActionReqDto.java b/src/main/java/org/example/qpin/domain/parking/dto/ParkingActionReqDto.java new file mode 100644 index 0000000..90482b8 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/parking/dto/ParkingActionReqDto.java @@ -0,0 +1,21 @@ +package org.example.qpin.domain.parking.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ParkingActionReqDto { + + @NotNull(message = "memberId는 필수입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; + + @NotNull(message = "parkingAreaId는 필수입니다.") + @Positive(message = "parkingAreaId는 양수여야 합니다.") + private Long parkingAreaId; + + private String type; +} diff --git a/src/main/java/org/example/qpin/domain/parking/dto/ParkingActionResDto.java b/src/main/java/org/example/qpin/domain/parking/dto/ParkingActionResDto.java new file mode 100644 index 0000000..47acb4d --- /dev/null +++ b/src/main/java/org/example/qpin/domain/parking/dto/ParkingActionResDto.java @@ -0,0 +1,11 @@ +package org.example.qpin.domain.parking.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ParkingActionResDto { + private Long parkingId; + private String message; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/dto/ParkingInfoReqDto.java b/src/main/java/org/example/qpin/domain/parking/dto/ParkingInfoReqDto.java new file mode 100644 index 0000000..e0263ae --- /dev/null +++ b/src/main/java/org/example/qpin/domain/parking/dto/ParkingInfoReqDto.java @@ -0,0 +1,14 @@ +package org.example.qpin.domain.parking.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ParkingInfoReqDto { + @NotNull(message = "memberId는 필수입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/dto/ParkingRegisterReqDto.java b/src/main/java/org/example/qpin/domain/parking/dto/ParkingRegisterReqDto.java new file mode 100644 index 0000000..40f91ed --- /dev/null +++ b/src/main/java/org/example/qpin/domain/parking/dto/ParkingRegisterReqDto.java @@ -0,0 +1,21 @@ +package org.example.qpin.domain.parking.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ParkingRegisterReqDto { + @NotNull(message = "memberId는 필수입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; + + @NotNull(message = "parkingAreaId는 필수입니다.") + @Positive(message = "parkingAreaId는 양수여야 합니다.") + private Long parkingAreaId; + + @NotNull(message = "주차 타입은 필수입니다.") + private String type; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/dto/ParkingReleaseReqDto.java b/src/main/java/org/example/qpin/domain/parking/dto/ParkingReleaseReqDto.java new file mode 100644 index 0000000..cdcaedd --- /dev/null +++ b/src/main/java/org/example/qpin/domain/parking/dto/ParkingReleaseReqDto.java @@ -0,0 +1,18 @@ +package org.example.qpin.domain.parking.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ParkingReleaseReqDto { + @NotNull(message = "memberId는 필수입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; + + @NotNull(message = "parkingAreaId는 필수입니다.") + @Positive(message = "parkingAreaId는 양수여야 합니다.") + private Long parkingAreaId; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/dto/ParkingSearchReqDto.java b/src/main/java/org/example/qpin/domain/parking/dto/ParkingSearchReqDto.java index 7b3b074..1ba4a6a 100644 --- a/src/main/java/org/example/qpin/domain/parking/dto/ParkingSearchReqDto.java +++ b/src/main/java/org/example/qpin/domain/parking/dto/ParkingSearchReqDto.java @@ -1,15 +1,22 @@ package org.example.qpin.domain.parking.dto; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; @Getter @Builder -// 주차장 검색 요청 public class ParkingSearchReqDto { - private Double latitude; // 위도 - private Double longitude; // 경도 - private Double distance; // 검색 기준 거리 - private String regionCode; // 지역코드 5자리 문자열 + @NotNull(message = "위도는 필수입니다.") + private Double latitude; + + @NotNull(message = "경도는 필수입니다.") + private Double longitude; + + @NotNull(message = "반경 정보는 필수입니다.") + private Double distance; + + @NotNull(message = "지역코드는 필수입니다.") + private String regionCode; } \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/parking/service/ParkingService.java b/src/main/java/org/example/qpin/domain/parking/service/ParkingService.java index 12c80b9..3c98bb6 100644 --- a/src/main/java/org/example/qpin/domain/parking/service/ParkingService.java +++ b/src/main/java/org/example/qpin/domain/parking/service/ParkingService.java @@ -14,6 +14,7 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.DefaultUriBuilderFactory; @@ -31,6 +32,9 @@ public class ParkingService { private final MemberRepository memberRepository; private final ScrapRepository scrapRepository; + @Value("${PUBLIC_API_KEY}") + private String apiKey; + public Member findMemberById(Long memberId) { return memberRepository.findById(memberId).orElseThrow(() -> new BadRequestException(ExceptionCode.NOT_FOUND_MEMBER_ID)); } @@ -53,12 +57,11 @@ public List findParkingNearby(Double myLatitude, Double myL // 공공포털에서 데이터 가져오기 final int page=1; final int perPage=150; - final String DECODING_KEY="yncOh3M5FtqbW1UwmQmkBKpkkyYqZMj1FddwHcalnFzVCFtnlwkDOhRPFHkhnJPRKYy4scMVfbJMxn954Ym/Eg=="; // 키 암호화 필요 - final String API_URL="https://api.odcloud.kr/api/15050093/v1/uddi:d19c8e21-4445-43fe-b2a6-865dff832e08?" + final String API_URL="https://api.data.go.kr/openapi/tn_pubr_prkplce_info_api" +"page=" + page +"&perPage=" + perPage +"&cond%5B%EC%A7%80%EC%97%AD%EC%BD%94%EB%93%9C%3A%3AEQ%5D=" + regionCode - +"&serviceKey=" + DECODING_KEY; + +"&serviceKey=" + apiKey; DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(API_URL); factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY); @@ -90,7 +93,7 @@ public List findParkingNearby(Double myLatitude, Double myL dataList = (JSONArray) jsonObject.get("data"); } catch (Exception e) { // JSON 파싱 오류 시 500번대 에러 반환 - throw new BadRequestException(ExceptionCode.INTERNAL_SEVER_ERROR); + throw new BadRequestException(ExceptionCode.SEVER_ERROR); } // 데이터가 없으면 빈 리스트로 반환 @@ -159,6 +162,7 @@ private static double rad2deg(double rad){ // 주차 등록하기 public Long postParking(Long memberId, Long parkingAreaId, String type) { Member member = findMemberById(memberId); + Parking parking = findParkingById(parkingAreaId); // 해당 멤버가 이미 주차 중인 상태인지 확인 if (member.isParking()) { @@ -167,7 +171,7 @@ public Long postParking(Long memberId, Long parkingAreaId, String type) { // 새로운 주차 정보 등록 Parking newParking = Parking.builder() - .parkingAreaId(parkingAreaId) + .parkingAreaId(parking.getParkingAreaId()) .type(type) .build(); parkingRepository.save(newParking); @@ -183,9 +187,10 @@ public Long postParking(Long memberId, Long parkingAreaId, String type) { // 주차 삭제하기 public Long deleteParking(Long memberId, Long parkingAreaId) { Member member = findMemberById(memberId); + Parking parking = findParkingById(parkingAreaId); // 주차 정보가 존재하는지 확인 - Parking parkingToDelete = parkingRepository.findParkingByParkingAreaIdAndMember_MemberId(parkingAreaId, memberId) + Parking parkingToDelete = parkingRepository.findParkingByParkingAreaIdAndMember_MemberId(parking.getParkingAreaId(), memberId) .orElseThrow(() -> new BadRequestException(ExceptionCode.NOT_FOUND_PARKING)); // 주차 삭제 diff --git a/src/main/java/org/example/qpin/domain/scrap/controller/ScrapController.java b/src/main/java/org/example/qpin/domain/scrap/controller/ScrapController.java index b8757b1..73902d8 100644 --- a/src/main/java/org/example/qpin/domain/scrap/controller/ScrapController.java +++ b/src/main/java/org/example/qpin/domain/scrap/controller/ScrapController.java @@ -1,53 +1,56 @@ package org.example.qpin.domain.scrap.controller; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.example.qpin.domain.scrap.dto.ScrapResponseDto; +import org.example.qpin.domain.scrap.dto.*; import org.example.qpin.domain.scrap.service.ScrapService; import org.example.qpin.global.common.response.CommonResponse; import org.example.qpin.global.common.response.ResponseCode; import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Controller @RestController @RequiredArgsConstructor @RequestMapping("scrap") +@Validated public class ScrapController { private final ScrapService scrapService; // [Post] 주차장 스크랩 추가 - @PostMapping("/{parkId}/{memberId}") - public CommonResponse> addScrap(@PathVariable("memberId") Long memberId, @PathVariable("parkId") Long parkId) { - Long newScrap = scrapService.postScrap(memberId, parkId); + @PostMapping + public CommonResponse addScrap(@Valid @RequestBody addScrapReqDto requestDto) { + Long newScrapId = scrapService.postScrap(requestDto.getMemberId(), requestDto.getParkId()); - Map result = new HashMap<>(); - result.put("parkingId", String.valueOf(newScrap)); - result.put("message", "즐겨찾기에 추가되었습니다."); + addScrapResDto responseDto = addScrapResDto.builder() + .scrapId(newScrapId) + .message("즐겨찾기에 추가되었습니다.") + .build(); - return new CommonResponse<>(ResponseCode.SUCCESS, result); + return new CommonResponse<>(ResponseCode.SUCCESS, responseDto); } // [Get] 스크랩 리스트 @GetMapping("/{memberId}") - public CommonResponse> getScrapByMember(@PathVariable Long memberId) { - List scrapList = scrapService.getScrapList(memberId); + public CommonResponse> getScrapByMember(@Valid @RequestBody getScrapReqDto requestDto) { + List scrapList = scrapService.getScrapList(requestDto.getMemberId()); return new CommonResponse<>(ResponseCode.SUCCESS, scrapList); } // [Delete] 스크랩 삭제 @DeleteMapping("/{scrapId}") - public CommonResponse> deleteScrap(@PathVariable Long scrapId) { - Long deletedScrap = scrapService.deleteScrap(scrapId); + public CommonResponse deleteScrap(@Valid @RequestBody deleteScrapReqDto requestDto) { + Long deletedScrap = scrapService.deleteScrap(requestDto.getScrapId()); - Map result = new HashMap<>(); - result.put("parkingId", String.valueOf(deletedScrap)); - result.put("message", "즐겨찾기에서 삭제되었습니다."); + deleteScrapResDto responseDto = deleteScrapResDto.builder() + .parkingId(deletedScrap) + .message("즐겨찾기에서 삭제되었습니다.") + .build(); - return new CommonResponse<>(ResponseCode.SUCCESS, result); + return new CommonResponse<>(ResponseCode.SUCCESS, responseDto); } } diff --git a/src/main/java/org/example/qpin/domain/scrap/dto/addScrapReqDto.java b/src/main/java/org/example/qpin/domain/scrap/dto/addScrapReqDto.java new file mode 100644 index 0000000..d7b6282 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/scrap/dto/addScrapReqDto.java @@ -0,0 +1,20 @@ +package org.example.qpin.domain.scrap.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class addScrapReqDto { + + @NotNull(message = "memberId는 필수 입력값입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; + + @NotNull(message = "parkId는 필수 입력값입니다.") + @Positive(message = "parkId는 양수여야 합니다.") + private Long parkId; + +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/scrap/dto/addScrapResDto.java b/src/main/java/org/example/qpin/domain/scrap/dto/addScrapResDto.java new file mode 100644 index 0000000..d76e7e9 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/scrap/dto/addScrapResDto.java @@ -0,0 +1,13 @@ +package org.example.qpin.domain.scrap.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class addScrapResDto { + + private Long scrapId; + private String message; + +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/scrap/dto/deleteScrapReqDto.java b/src/main/java/org/example/qpin/domain/scrap/dto/deleteScrapReqDto.java new file mode 100644 index 0000000..cb00008 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/scrap/dto/deleteScrapReqDto.java @@ -0,0 +1,16 @@ +package org.example.qpin.domain.scrap.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class deleteScrapReqDto { + + @NotNull(message = "scrapId는 필수 입력값입니다.") + @Positive(message = "scrapId는 양수여야 합니다.") + private Long scrapId; + +} diff --git a/src/main/java/org/example/qpin/domain/scrap/dto/deleteScrapResDto.java b/src/main/java/org/example/qpin/domain/scrap/dto/deleteScrapResDto.java new file mode 100644 index 0000000..46c457e --- /dev/null +++ b/src/main/java/org/example/qpin/domain/scrap/dto/deleteScrapResDto.java @@ -0,0 +1,13 @@ +package org.example.qpin.domain.scrap.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class deleteScrapResDto { + + private Long parkingId; + private String message; + +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/scrap/dto/getScrapReqDto.java b/src/main/java/org/example/qpin/domain/scrap/dto/getScrapReqDto.java new file mode 100644 index 0000000..e44f871 --- /dev/null +++ b/src/main/java/org/example/qpin/domain/scrap/dto/getScrapReqDto.java @@ -0,0 +1,15 @@ +package org.example.qpin.domain.scrap.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class getScrapReqDto { + + @NotNull(message = "memberId는 필수 입력값입니다.") + @Positive(message = "memberId는 양수여야 합니다.") + private Long memberId; +} \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/scrap/dto/ScrapResponseDto.java b/src/main/java/org/example/qpin/domain/scrap/dto/getScrapResDto.java similarity index 83% rename from src/main/java/org/example/qpin/domain/scrap/dto/ScrapResponseDto.java rename to src/main/java/org/example/qpin/domain/scrap/dto/getScrapResDto.java index 4ea22c5..98b9802 100644 --- a/src/main/java/org/example/qpin/domain/scrap/dto/ScrapResponseDto.java +++ b/src/main/java/org/example/qpin/domain/scrap/dto/getScrapResDto.java @@ -5,7 +5,7 @@ @Getter @Builder -public class ScrapResponseDto { +public class getScrapResDto { private Long scrapId; private Long parkingId; } \ No newline at end of file diff --git a/src/main/java/org/example/qpin/domain/scrap/service/ScrapService.java b/src/main/java/org/example/qpin/domain/scrap/service/ScrapService.java index 3070eb9..313fd52 100644 --- a/src/main/java/org/example/qpin/domain/scrap/service/ScrapService.java +++ b/src/main/java/org/example/qpin/domain/scrap/service/ScrapService.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.example.qpin.domain.member.entity.Member; import org.example.qpin.domain.parking.entity.Parking; -import org.example.qpin.domain.scrap.dto.ScrapResponseDto; +import org.example.qpin.domain.scrap.dto.getScrapResDto; import org.example.qpin.domain.scrap.entity.Scrap; import org.example.qpin.global.common.repository.MemberRepository; import org.example.qpin.global.common.repository.ParkingRepository; @@ -47,13 +47,12 @@ public Long postScrap(Long memberId, Long parkId) { return newScrap.getScrapId(); } - public List getScrapList(Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new RuntimeException("회원 없음")); + public List getScrapList(Long memberId) { + Member member = findMemberById(memberId); List scrapList = scrapRepository.findAllByMember(member); return scrapList.stream() - .map(scrap -> ScrapResponseDto.builder() + .map(scrap -> getScrapResDto.builder() .scrapId(scrap.getScrapId()) .parkingId(scrap.getParking().getParkingAreaId()) .build()) diff --git a/src/main/java/org/example/qpin/global/exception/ExceptionCode.java b/src/main/java/org/example/qpin/global/exception/ExceptionCode.java index e6c011f..4150387 100644 --- a/src/main/java/org/example/qpin/global/exception/ExceptionCode.java +++ b/src/main/java/org/example/qpin/global/exception/ExceptionCode.java @@ -30,7 +30,8 @@ public enum ExceptionCode { NOT_FOUND_PHOTO(5104, "사진을 찾을 수 없습니다."), // 주차 에러 - NOT_FOUND_PARKING(6001, "요청한 주차ID와 멤버ID에 해당하는 정보가 존재하지 않습니다."), + NOT_FOUND_PARKING(6000, "요청한 ID에 해당하는 주차장이 존재하지 않습니다."), + NOT_FOUND_PARKING_INFO(6001, "요청한 주차ID와 멤버ID에 해당하는 정보가 존재하지 않습니다."), INVALID_USER_NAME(8001, "존재하지 않는 사용자입니다."), INVALID_PASSWORD(8002, "비밀번호가 일치하지 않습니다."), @@ -52,6 +53,7 @@ public enum ExceptionCode { NOT_FOUND_REFRESH_TOKEN(9106, "RefreshToken이 존재하지 않습니다."), INVALID_AUTHORITY(9201, "해당 요청에 대한 접근 권한이 없습니다."), + SEVER_ERROR(9990, "공공데이터를 불러오는 중 오류가 발생했습니다."), INTERNAL_SEVER_ERROR(9999, "서버 에러가 발생하였습니다. 관리자에게 문의해 주세요."); private final int code; diff --git a/src/main/java/org/example/qpin/global/jwt/JWTUtil.java b/src/main/java/org/example/qpin/global/jwt/JWTUtil.java index a9db875..dbb3822 100644 --- a/src/main/java/org/example/qpin/global/jwt/JWTUtil.java +++ b/src/main/java/org/example/qpin/global/jwt/JWTUtil.java @@ -4,71 +4,68 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; -import lombok.RequiredArgsConstructor; -import org.example.qpin.domain.member.entity.Member; -import org.example.qpin.global.common.repository.MemberRepository; -import org.example.qpin.global.exception.BadRequestException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Date; -import java.util.Optional; - -import static org.example.qpin.global.exception.ExceptionCode.INVALID_USER_NAME; @Component public class JWTUtil { - private SecretKey secretKey; + private final SecretKey secretKey; - public JWTUtil(@Value("${spring.jwt.secret}")String secret) { + public JWTUtil(@Value("${JWT_SECRET}") String secret) { + if (secret == null || secret.isEmpty() || secret.equals("${JWT_SECRET}")) { + throw new IllegalArgumentException("JWT secret cannot be null, empty, or unresolvable placeholder."); + } // 변수 잘 넘어오는지 확인 + byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8); + this.secretKey = Keys.hmacShaKeyFor(keyBytes); + } - secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm()); + private Claims getClaims(String token) { + return Jwts + .parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token) + .getPayload(); } public String getUsername(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class); + return getClaims(token).get("username", String.class); } public String getRole(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); + return getClaims(token).get("role", String.class); } public String getEmail(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("email", String.class); + return getClaims(token).get("email", String.class); } public String getPhoneNumber(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("phoneNumber", String.class); + return getClaims(token).get("phoneNumber", String.class); } public String getCategory(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("category", String.class); + return getClaims(token).get("category", String.class); } - // jwt 만료 여부 확인 : 토큰 만료 시, true public Boolean isExpired(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + return getClaims(token).getExpiration().before(new Date()); } public String createJwt(String category, String username, String role, Long expiredMs) { - - return Jwts.builder() + return Jwts + .builder() .claim("category", category) .claim("username", username) .claim("role", role) - .issuedAt(new Date(System.currentTimeMillis())) // 발급 시간 - .expiration(new Date(System.currentTimeMillis() + expiredMs)) // 만료 시간 - .signWith(secretKey) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + expiredMs)) + .signWith(secretKey, SignatureAlgorithm.HS256) // HS256 알고리즘 명시 .compact(); } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 36b3b7d..0744c50 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,13 +1,28 @@ spring: profiles: - active: local # 디폴트로 로드 + active: local # 디폴트 활성 프로필 설정 group: local: local, common, secret blue: blue, common, secret green: green, common, secret + datasource: + url: jdbc:mysql://${QPIN_SERVER_IP}:3306/qpin + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + hibernate: + ddl-auto: update + show-sql: true + properties: + hibernate: + format_sql: true + dialect: org.hibernate.dialect.MySQL8Dialect + server: - env: blue + env: blue # 이 값은 docker-compose.yml의 SERVER_ENV 환경 변수로 오버라이드될 수 있습니다. --- @@ -31,7 +46,7 @@ spring: server: port: 8080 - serverAddress: 43.200.208.136 + serverAddress: ${QPIN_SERVER_IP} serverName: blue_server @@ -44,7 +59,7 @@ spring: server: port: 8081 - serverAddress: 43.200.208.136 + serverAddress: ${QPIN_SERVER_IP} serverName: green_server @@ -54,4 +69,5 @@ spring: config: activate: on-profile: common - + jwt: + secret: ${JWT_SECRET} \ No newline at end of file