Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
519f3fc
유효성 검사 추가 및 DTO 분리
songwol38 May 24, 2025
e257d8a
CD 정의
songwol38 Jul 6, 2025
50ba8f1
CD 수정
songwol38 Jul 7, 2025
424da43
공공데이터 API KEY 암호화
songwol38 Jul 15, 2025
bbc3f7e
fix : 응답 코드 수정
songwol38 Jul 15, 2025
55ffbea
Merge branch 'main' into feat/#8
songwol38 Jul 15, 2025
08eb6f7
fix : 타입 오류 수정
songwol38 Jul 15, 2025
1c68d06
feat : env 파일 추가 로직 구현
songwol38 Jul 15, 2025
6aa70b0
feat : DB 연결 로직 추가
songwol38 Jul 15, 2025
623cc7b
fix : DB 설정 추가
songwol38 Jul 15, 2025
9359609
fix : 하드코딩
songwol38 Jul 15, 2025
7fe8457
fix : 들여쓰기 수정
songwol38 Jul 15, 2025
11f82fd
fix : yml 수정
songwol38 Jul 15, 2025
6fe29e0
fix : yml 수정
songwol38 Jul 15, 2025
52c943e
fix : 무중단 배포 비활성화
songwol38 Jul 15, 2025
8959db0
fix : dburl 설정
songwol38 Jul 15, 2025
3ab9a12
fix : env 불러오기
songwol38 Jul 15, 2025
85ebf35
fix : 타입 수정
songwol38 Jul 15, 2025
c95c11f
fix : 엔티티와 맞춰서 수정
songwol38 Jul 16, 2025
75e157c
잠시 비활성화
songwol38 Jul 16, 2025
3082bf9
fix : 의존성 버전 낮춤
songwol38 Jul 16, 2025
4c6859d
fix : 의존성 수정
songwol38 Jul 16, 2025
d6f9d7a
fix : 의존성 수정
songwol38 Jul 16, 2025
3fc395c
fix : jwt 임시 수정
songwol38 Jul 16, 2025
2be3449
env 명시
songwol38 Jul 16, 2025
087e2e2
로그 추가
songwol38 Jul 16, 2025
0eaaf9b
부족한 API 키 추가
songwol38 Jul 16, 2025
f8f8ffe
파일 삭제
songwol38 Jul 16, 2025
2eda089
디코딩 수정
songwol38 Jul 16, 2025
9f2fac5
시크릿 yml 제거
songwol38 Jul 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 59 additions & 40 deletions .github/workflows/github-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
#
Expand All @@ -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
Expand Down
11 changes: 9 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
ENTRYPOINT ["sh", "-c", "java -Dspring.profiles.active=$SPRING_PROFILES_ACTIVE -Dserver.env=$SERVER_ENV -jar app.jar"]
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,30 +18,32 @@ public class CarPhotoController {

// [Post] 차량 사진 저장
@PostMapping
public CommonResponse<Map<String, String>> saveCarPhoto(@RequestBody CarPhotoRequestDto carPhotoRequestDto) {
Long newPhoto = carPhotoService.saveCarPhoto(carPhotoRequestDto);
public CommonResponse<SavePhotoResDto> savePhoto(@RequestBody SavePhotoReqDto reqDto) {
Long newPhoto = carPhotoService.saveCarPhoto(reqDto);

Map<String, String> 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<List<CarPhotoResponseDto>> getCarPhotoList(@RequestParam Long memberId) {
List<CarPhotoResponseDto> carPhotoList = carPhotoService.getCarPhotoList(memberId);
public CommonResponse<List<GetPhotoResDto>> getCarPhotoList(@Valid @RequestBody GetPhotoReqDto reqDto) {
List<GetPhotoResDto> carPhotoList = carPhotoService.getCarPhotoList(reqDto.getMemberId());
return new CommonResponse<>(ResponseCode.SUCCESS, carPhotoList);
}

@DeleteMapping("/{photoId}")
public CommonResponse<Map<String, String>> deleteCarPhoto(@PathVariable Long photoId) {
Long deletePhoto = carPhotoService.deleteCarPhoto(photoId);
public CommonResponse<DeletePhotoResDto> deleteCarPhoto(@Valid @RequestBody DeletePhotoReqDto reqDto) {
Long deletePhoto = carPhotoService.deleteCarPhoto(reqDto.getPhotoId());

Map<String, String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down
Loading
Loading