Skip to content

Commit

Permalink
전남대 BE_26조 10주차 코드리뷰 요청 (#95)
Browse files Browse the repository at this point in the history
* [Weekly/10/Test/Member-Service] 멤버 도메인 서비스 유닛테스트 (#70)

* test: 멤버 도메인 application 패키지 테스트 추가

* refactor: early return 리팩터링

* [Weekly/10/All/fetchJoin] 연관관계 설정 수정, 페치 조인, 배치사이즈 설정, 페이지네이션 (#71)

* feature: reservation 페치조인, 페이징

* feat: ad 페치조인, 페이징

* feat: curation 페치조인, 페이징

* feat: event 페치조인, 페이징

* refactor: todo 추가

* fix: JPQL 수정

* fix: oneToMany관계 page 사용시 fetch join 수정

* feat: 정렬기준 createdDate가 아닌 ID 로 수정

* [Weekly/10/All/Set-Authorization] authorization 로직 추가 (#72)

* [Weekly/10/Feature/Authorization] 모든 컨트롤러 authorization 적용 및 서비스 검증 로직 추가 (#73)

* [Weekly/10/Deploy/CICD] CI/CD 관련 스크립트 추가 (#74)

* [Weekly/10/Test/Event] Event 테스트(1차) 및 TestData 리펙터링 (#77)

* [Weekly/10/Chore/Deploy] CI/CD 스크립트 그룹화 (#78)

* chore: github action jobs 그룹화

* chore: Dockerfile 못찾는 문제 수정

* chore: docker build 오류 수정

* chore: docker build 오류 수정 2

* chore: docker build 오류 수정 3

* [Weekly/10/Test/Authorization] 큐레이션 유닛 테스트 (#79)

* [Weekly/10/Test/Event] Event 단위테스트 (#80)

* [Weekly/10/Chore/Deploy] PR시 secrets에 접근 가능하도록 수정 (#81)

* refactor: Like Controller return값 리팩터링 (#82)

* [Weekly/10/Chore/Deploy] push일 때만 Deploy되도록 수정 (#83)

* chore: push일 때만 Deploy되도록 수정

* chore: push일 때만 Docker push 되도록 수정

* [Weekly/10/Test/Member-Controllers] add MemberControllerUnitTest Template (#84)

* feat: 큐레이션 컨트롤러 유닛테스트 (#85)

* [Weekly/10/Test/Refactor-MemberId] test 관련 member 도메인의 아이디 상수화 이외 1개 (#86)

* refactor: test 관련 memberId 상수화

* chore: 이제는 github action시 테스트도 수행함

* [Weekly/10/Chore/Deploy] Github Action 스크립트 수정 (#89)

* fix: attempt 1

* fix: attempt 2 (log show)

* fix: attempt 3 (add secrets.env)

* feat: .env 업데이트 기능 추가

* feat: jobs 단계 세분화

* fix: test fail fix attempt 1

* fix: docker build fail fix attempt 2

* chore: add echo

* chore: rename jobs step naming

* [Weekly/10/Test/CurationController] curationControllerUnitTest (#88)

* [Weekly/10/Reservation/unitTest] Reservation unit 테스트 추가 

* feat: reservation unit test

* feat: 행사명으로 검색 로직 추가

* feat: url을 이미지로 저장하는 로직

---------

Co-authored-by: 조홍식 <[email protected]>
Co-authored-by: ariimo <[email protected]>
  • Loading branch information
3 people authored Nov 10, 2024
1 parent 2342924 commit 2edb5d8
Show file tree
Hide file tree
Showing 104 changed files with 3,463 additions and 776 deletions.
188 changes: 188 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: CI/CD Github Action

on:
push:
branches: [ "Master", "Weekly/*" ]
pull_request_target:
branches: [ "Master", "Weekly/*" ]

permissions:
contents: read

env:
GOOGLE_CLIENT_ID : ${{ secrets.ENV_GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.ENV_GOOGLE_CLIENT_SECRET }}
GOOGLE_REDIRECT_URI : ${{ secrets.ENV_GOOGLE_REDIRECT_URI }}
JWT_SECRET : ${{ secrets.ENV_JWT_SECRET }}
KAKAO_CLIENT_ID : ${{ secrets.ENV_KAKAO_CLIENT_ID }}
KAKAO_CLIENT_SECRET : ${{ secrets.ENV_KAKAO_CLIENT_SECRET }}
KAKAO_REDIRECT_URI : ${{ secrets.ENV_KAKAO_REDIRECT_URI }}
KAKAOPAY_SECRET_KEY : ${{ secrets.ENV_KAKAOPAY_SECRET_KEY }}

jobs:
## 1단계: 프로젝트 빌드
Build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'corretto'

- name: Gradle Caching (for faster build)
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build Application
run: |
chmod +x gradlew
./gradlew clean build -x test
- name: Store build failure reports (execute when build fail)
if: failure()
uses: actions/upload-artifact@v3
with:
name: build-failure-reports
path: |
**/build/reports/
- name: Store build artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: build/libs/*.jar

## 2단계: 테스트 실행
Test:
runs-on: ubuntu-22.04
needs: Build # Build 단계가 완료되어야 실행됨
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'corretto'

- name: Gradle Caching (for faster build)
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Test Application
run: |
chmod +x gradlew
./gradlew test
- name: Store test failure reports (execute when test fail)
if: failure()
uses: actions/upload-artifact@v3
with:
name: test-failure-reports
path: |
**/build/test-results/
## 3단계: Docker 빌드 및 푸시
Docker-Build:
runs-on: ubuntu-22.04
needs: Test # Test 단계가 성공적으로 완료되어야 실행됨
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4

- name: Download Build Artifacts
uses: actions/download-artifact@v3
with:
name: build-artifacts
path: build/libs

- name: Docker Hub Login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Docker Build
run: docker build -f Dockerfile --build-arg DEPENDENCY=build/dependency -t ${{ secrets.DOCKER_REPO_FULLNAME }} .

- name: Docker Push
run: docker push ${{ secrets.DOCKER_REPO_FULLNAME }}

## 4단계: 서버에 배포
Deploy:
runs-on: ubuntu-22.04
needs: Docker-Build
if: github.event_name == 'push'
steps:
- name: Update .env
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
script: |
echo "GOOGLE_CLIENT_ID=${{ secrets.ENV_GOOGLE_CLIENT_ID }}" > ~/.env
echo "GOOGLE_CLIENT_SECRET=${{ secrets.ENV_GOOGLE_CLIENT_SECRET }}" >> ~/.env
echo "GOOGLE_REDIRECT_URI=${{ secrets.ENV_GOOGLE_REDIRECT_URI }}" >> ~/.env
echo "JWT_SECRET=${{ secrets.ENV_JWT_SECRET }}" >> ~/.env
echo "KAKAO_CLIENT_ID=${{ secrets.ENV_KAKAO_CLIENT_ID }}" >> ~/.env
echo "KAKAO_CLIENT_SECRET=${{ secrets.ENV_KAKAO_CLIENT_SECRET }}" >> ~/.env
echo "KAKAO_REDIRECT_URI=${{ secrets.ENV_KAKAO_REDIRECT_URI }}" >> ~/.env
echo "KAKAOPAY_SECRET_KEY=${{ secrets.ENV_KAKAOPAY_SECRET_KEY }}" >> ~/.env
echo "Environment setup has been completed."
- name: Pull New Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker pull ${{ secrets.DOCKER_REPO_FULLNAME }}

- name: Stop Old Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker stop would-you-in

- name: Run New Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker run --rm -d -p 80:8080 --env-file ~/.env --name would-you-in ${{ secrets.DOCKER_REPO_FULLNAME }}

- name: Clean-Up Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker image prune -f
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ out/

### static images
/src/main/resources/staticimages/

### env file
.env
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# open jdk 21 버전의 환경을 구성한다.
FROM openjdk:21-jdk-slim

# build가 될 때 JAR_FILE이라는 변수 명에 build/libs/*.jar 선언
# build/libs - gradle로 빌드했을 때 jar 파일이 생성되는 경로임
ARG JAR_FILE=build/libs/*.jar

# JAR_FILE을 agaproject.jar로 복사 (이 부분(.jar)은 개발환경에 따라 다름)
COPY ${JAR_FILE} wouldyouin.jar

# 운영 및 개발에서 사용되는 환경 설정을 분리한다.
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=deploy", "/wouldyouin.jar"]
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageResponse;
import org.ktc2.cokaen.wouldyouin.Image.application.ImageServiceFactory;
import org.ktc2.cokaen.wouldyouin.Image.application.ImageStorage;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponse;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponseBody;
import org.ktc2.cokaen.wouldyouin._common.exception.FailToReadImageException;
import org.ktc2.cokaen.wouldyouin.auth.Authorize;
import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier;
import org.ktc2.cokaen.wouldyouin.member.persist.MemberType;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -29,8 +33,10 @@ public class ImageController {

private final ImageServiceFactory imageServiceFactory;

// Todo: authorize
@PostMapping("/images")
public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(@RequestParam List<MultipartFile> images,
public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(
@RequestParam List<MultipartFile> images,
@RequestParam(value = "type") ImageDomain imageDomain) {
return ApiResponse.ok(imageServiceFactory.getImageServiceByImageType(imageDomain).saveAndCreateImages(images));
}
Expand All @@ -44,8 +50,11 @@ public ResponseEntity<byte[]> getImage(@PathVariable String path) {
}
}

// Todo: authorize
@DeleteMapping("/images/{id}")
public ResponseEntity<ApiResponseBody<Void>> deleteImage(@PathVariable Long id, @RequestParam ImageDomain imageDomain) {
public ResponseEntity<ApiResponseBody<Void>> deleteImage(
@PathVariable Long id,
@RequestParam(value = "type") ImageDomain imageDomain) {
imageServiceFactory.getImageServiceByImageType(imageDomain).deleteAndDelete(id);
return ApiResponse.noContent();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ public class ImageResponse {
private LocalDateTime createdDate;
private Long size;

public static ImageResponse from(Image image) {
public static ImageResponse from(Image image, String domainName) {
System.out.println("도메인" + domainName);
return ImageResponse.builder()
.id(image.getId())
.url(image.getUrl())
.url(domainName + image.getUrl())
.size(image.getSize())
.createdDate(image.getCreatedDate())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
@RequiredArgsConstructor
public class AdvertisementImageService extends ImageService<AdvertisementImage> {

@Value("${image.upload.ad.sub-path}")
private String subPath;
private final ImageStorage imageStorage;
private final AdvertisementImageRepository adImageRepository;
@Value("${image.upload.ad.sub-path}")
private String subPath;

@Override
protected ImageRepository<AdvertisementImage> getImageRepository() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected String getSubPath() {
@Override
protected CurationImage toEntity(ImageRequest imageRequest) {
return CurationImage.builder()
.name(imageRequest.getUrl())
.url(imageRequest.getUrl())
.size(imageRequest.getSize())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package org.ktc2.cokaen.wouldyouin.Image.application;

import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.api.ImageDomain;
import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageRequest;
import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage;
import org.ktc2.cokaen.wouldyouin.Image.persist.EventImage;
import org.ktc2.cokaen.wouldyouin.Image.persist.EventImageRepository;
import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityParamIsNullException;
import org.ktc2.cokaen.wouldyouin.advertisement.persist.Advertisement;
import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCard;
import org.ktc2.cokaen.wouldyouin.event.persist.Event;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -44,7 +39,7 @@ protected String getSubPath() {

@Override
protected EventImage toEntity(ImageRequest imageRequest) {
return EventImage.builder().name(imageRequest.getUrl()).size(imageRequest.getSize()).build();
return EventImage.builder().url(imageRequest.getUrl()).size(imageRequest.getSize()).build();
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -20,6 +21,9 @@ public abstract class ImageService<T extends Image> {
@Autowired
protected ImageStorage imageStorage;

@Value("${spring.wouldyouin-domain-name}")
private String domainName;

protected abstract ImageRepository<T> getImageRepository();

protected abstract ImageDomain getImageDomain();
Expand All @@ -28,13 +32,13 @@ public abstract class ImageService<T extends Image> {

protected abstract T toEntity(ImageRequest imageRequest);

public T getByIdOrThrow(Long id) {
public T getById(Long id) {
return getImageRepository().findById(id)
.orElseThrow(() -> new EntityNotFoundException(getImageDomain().name() + " Image"));
}

protected ImageResponse create(ImageRequest imageRequest) {
return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)));
return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)), domainName);
}

protected void delete(Long id) {
Expand Down
Loading

0 comments on commit 2edb5d8

Please sign in to comment.