From 1be2a6c0ba26f50a222c131f9207d826d33cd94b Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:14:28 +0900 Subject: [PATCH 01/37] =?UTF-8?q?chore:=20chatgpt=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/code-review.yml diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml new file mode 100644 index 00000000..c6afe6b2 --- /dev/null +++ b/.github/workflows/code-review.yml @@ -0,0 +1,20 @@ +name: Code Review From ChatGPT + +permissions: + contents: read + pull-requests: write + +on: + pull_request: + types: [opened, synchronize] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: anc95/ChatGPT-CodeReview@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPEN_API_KEY }} + LANGUAGE: Korean + MODEL: gpt-3.5-turbo \ No newline at end of file From b3af25f667ca2c63a2e7129a04b5a5fe5b5c1d0d Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:17:07 +0900 Subject: [PATCH 02/37] =?UTF-8?q?fix:=20=EC=97=B0=EB=8F=99=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index c6afe6b2..08c2eeb8 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -15,6 +15,6 @@ jobs: - uses: anc95/ChatGPT-CodeReview@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPEN_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} LANGUAGE: Korean MODEL: gpt-3.5-turbo \ No newline at end of file From b454e129f775223ee45d2d7d6fe9137db1c651ee Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:31:25 +0900 Subject: [PATCH 03/37] =?UTF-8?q?Chore:=20=EB=A9=94=EC=9D=B8=20PR=20?= =?UTF-8?q?=EC=8B=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EC=A0=81=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC,=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=8B=A4=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..6a6eeebe --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,44 @@ +name: log4u-build +on: + push: + branches: + - dev # dev 브랜치 push + pull_request: + branches: + - main # main pr + types: [ opened, synchronize, reopened ] +jobs: + build: + name: Build and analyze + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'zulu' # Alternative distribution options are available + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + DB_URL: ${{ secrets.DB_URL }} # Database URL + DB_USERNAME: ${{ secrets.DB_USERNAME }} # Database username + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Database password + run: | + chmod +x ./gradlew + ./gradlew build jacocoTestReport sonar --info \ No newline at end of file From f421a2ff18fa752e756595cdbb5b3361213305d0 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:31:55 +0900 Subject: [PATCH 04/37] =?UTF-8?q?Chore:=20=EB=8D=B0=EC=9D=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=95=84=EC=B9=B4=EC=9D=B4=EB=B9=99=20yml=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/daily-archive.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/daily-archive.yml diff --git a/.github/workflows/daily-archive.yml b/.github/workflows/daily-archive.yml new file mode 100644 index 00000000..a201e782 --- /dev/null +++ b/.github/workflows/daily-archive.yml @@ -0,0 +1,28 @@ +name: Archive dev branch daily + +on: + schedule: + - cron: "59 14 * * *" # 한국 시간(KST) 23:59 (UTC+14:59) + workflow_dispatch: # 수동 실행 가능 + +jobs: + archive: + name: Archive dev branch + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # 모든 브랜치 가져오기 + + - name: Set archive branch name + id: date + run: echo "BRANCH_NAME=archive-$(date +'%Y-%m-%d')" >> $GITHUB_ENV + + - name: Create new archive branch + run: | + git checkout dev + git pull origin dev + git checkout -b ${{ env.BRANCH_NAME }} + git push origin ${{ env.BRANCH_NAME }} \ No newline at end of file From 7f8bd7f76292fb8f3e7b403b5c0de41273e2d0e5 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:32:36 +0900 Subject: [PATCH 05/37] =?UTF-8?q?Chore:=20Dev=20=EB=B8=8C=EB=9E=9C?= =?UTF-8?q?=EC=B9=98=20push=20=EC=98=B5=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a6eeebe..ca8c9427 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,8 @@ name: log4u-build on: - push: - branches: - - dev # dev 브랜치 push +# push: +# branches: +# - dev # dev 브랜치 push pull_request: branches: - main # main pr From 7830aee14a2afe013e762517cdca051bb82f5461 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:45:39 +0900 Subject: [PATCH 06/37] =?UTF-8?q?chore:=20jacoco,=20sonarcloud,=20checksty?= =?UTF-8?q?le=20=EC=98=B5=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index c412ca78..0ad7d420 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,9 @@ plugins { java id("org.springframework.boot") version "3.4.4" id("io.spring.dependency-management") version "1.1.7" + id("org.sonarqube") version "6.0.1.5171" + jacoco + checkstyle } group = "com.example" @@ -50,3 +53,26 @@ dependencies { tasks.withType { useJUnitPlatform() } + +tasks.jacocoTestReport { + reports { + xml.required = true + csv.required = false + } +} + +checkstyle { + configFile = file("${rootDir}/naver-checkstyle-rules.xml") + configProperties["suppressionFile"] = "${rootDir}/naver-checkstyle-suppressions.xml" + toolVersion = "9.2" +} + +sonar { + properties { + property("sonar.projectKey", "sapiens2000-dev_simple-sns") + property("sonar.organization", "sapiens2000-dev") + property("sonar.host.url", "https://sonarcloud.io") + property("sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacoco/test/jacocoTestReport.xml") + property("sonar.java.checkstyle.reportPaths", "build/reports/checkstyle/main.xml") + } +} \ No newline at end of file From b9d24272b19be598d51216a2c8cf65cb77590463 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 10:45:55 +0900 Subject: [PATCH 07/37] =?UTF-8?q?chore:=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=EB=B6=84=EC=84=9D=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- naver-checkstyle-rules.xml | 433 ++++++++++++++++++++++++++++++ naver-checkstyle-suppressions.xml | 7 + 2 files changed, 440 insertions(+) create mode 100644 naver-checkstyle-rules.xml create mode 100644 naver-checkstyle-suppressions.xml diff --git a/naver-checkstyle-rules.xml b/naver-checkstyle-rules.xml new file mode 100644 index 00000000..dafbb4d1 --- /dev/null +++ b/naver-checkstyle-rules.xml @@ -0,0 +1,433 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/naver-checkstyle-suppressions.xml b/naver-checkstyle-suppressions.xml new file mode 100644 index 00000000..3f11e0cd --- /dev/null +++ b/naver-checkstyle-suppressions.xml @@ -0,0 +1,7 @@ + + + + + From 6e80989d6beea2d83d51afa2e141bcf2fc517e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:21:33 +0900 Subject: [PATCH 08/37] =?UTF-8?q?feat:=20=EC=A0=84=EC=97=AD=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=ED=81=B4=EB=9E=98=EC=8A=A4(Gl?= =?UTF-8?q?obalExceptionHandler)=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/advice/GlobalExceptionHandler.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/main/java/com/example/log4u/common/advice/GlobalExceptionHandler.java diff --git a/src/main/java/com/example/log4u/common/advice/GlobalExceptionHandler.java b/src/main/java/com/example/log4u/common/advice/GlobalExceptionHandler.java new file mode 100644 index 00000000..7f9e2153 --- /dev/null +++ b/src/main/java/com/example/log4u/common/advice/GlobalExceptionHandler.java @@ -0,0 +1,114 @@ +package com.example.log4u.common.advice; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; +import org.springframework.validation.BindException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.example.log4u.common.exception.ApiErrorResponse; +import com.example.log4u.common.exception.CommonErrorCode; +import com.example.log4u.common.exception.base.ErrorCode; +import com.example.log4u.common.exception.base.ServiceException; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + + @Override + public ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException e, + @NonNull HttpHeaders headers, + @NonNull HttpStatusCode status, + @NonNull WebRequest request) { + HttpServletRequest servletRequest = ((ServletWebRequest)request).getRequest(); + + String requestUrl = servletRequest.getRequestURI(); + String httpMethod = servletRequest.getMethod(); + List errors = e.getBindingResult() + .getFieldErrors() + .stream() + .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()) + .collect(Collectors.toList()); + + log.warn("Validation failed for request to {} {}. Errors: {}", + httpMethod, requestUrl, errors); + CommonErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER; + return handleExceptionInternal(e, errorCode); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgument(IllegalArgumentException e) { + String location = getExceptionLocation(e); + + log.warn("Illegal argument encountered at {}: {}", location, e.getMessage()); + + CommonErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER; + return handleExceptionInternal(errorCode); + } + + @ExceptionHandler(ServiceException.class) + public ResponseEntity handleGiveMeTiConException(ServiceException e) { + String location = getExceptionLocation(e); + log.warn("Error invoke in our app at {}: {} ErrorCode: {}", location, e.getMessage(), e.getErrorCode()); + ErrorCode errorCode = e.getErrorCode(); + return handleExceptionInternal(errorCode); + } + + @ExceptionHandler({Exception.class}) + public ResponseEntity handleAllException(Exception e) { + String location = getExceptionLocation(e); + log.warn("Unhandled exception occurred at {}: {}", location, e.getMessage()); + + CommonErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR; + return handleExceptionInternal(errorCode); + } + + private ResponseEntity handleExceptionInternal(ErrorCode errorCode) { + return ResponseEntity.status(errorCode.getHttpStatus()) + .body(makeErrorResponse(errorCode)); + } + + private ApiErrorResponse makeErrorResponse(ErrorCode errorCode) { + return ApiErrorResponse.builder() + .errorMessage(errorCode.getErrorMessage()) + .errorCode(errorCode.getHttpStatus().value()) + .build(); + } + + private ResponseEntity handleExceptionInternal(BindException e, ErrorCode errorCode) { + return ResponseEntity.status(errorCode.getHttpStatus()) + .body(makeErrorResponse(e, errorCode)); + } + + private ApiErrorResponse makeErrorResponse(BindException e, ErrorCode errorCode) { + List validationErrorList = e.getBindingResult() + .getFieldErrors() + .stream() + .map(ApiErrorResponse.ValidationError::of) + .collect(Collectors.toList()); + + return ApiErrorResponse.builder() + .errorMessage(errorCode.getErrorMessage()) + .errorCode(errorCode.getHttpStatus().value()) + .errors(validationErrorList) + .build(); + } + + private String getExceptionLocation(Exception e) { + StackTraceElement element = e.getStackTrace()[0]; + return element.getClassName() + "." + element.getMethodName() + ":" + element.getLineNumber(); + } +} From ec1a1cd8cb71bd3b60aaa0076398e917bf57cddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:22:27 +0900 Subject: [PATCH 09/37] =?UTF-8?q?feat:=20=EB=AA=A8=EB=93=A0=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EA=B3=B5=ED=86=B5=20=EC=98=88=EC=99=B8=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B0=8F=20ServiceExcep?= =?UTF-8?q?tion=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log4u/common/exception/base/ErrorCode.java | 9 +++++++++ .../common/exception/base/ServiceException.java | 15 +++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/main/java/com/example/log4u/common/exception/base/ErrorCode.java create mode 100644 src/main/java/com/example/log4u/common/exception/base/ServiceException.java diff --git a/src/main/java/com/example/log4u/common/exception/base/ErrorCode.java b/src/main/java/com/example/log4u/common/exception/base/ErrorCode.java new file mode 100644 index 00000000..d55237eb --- /dev/null +++ b/src/main/java/com/example/log4u/common/exception/base/ErrorCode.java @@ -0,0 +1,9 @@ +package com.example.log4u.common.exception.base; + +import org.springframework.http.HttpStatus; + +public interface ErrorCode { + String name(); + HttpStatus getHttpStatus(); + String getErrorMessage(); +} diff --git a/src/main/java/com/example/log4u/common/exception/base/ServiceException.java b/src/main/java/com/example/log4u/common/exception/base/ServiceException.java new file mode 100644 index 00000000..060f128d --- /dev/null +++ b/src/main/java/com/example/log4u/common/exception/base/ServiceException.java @@ -0,0 +1,15 @@ +package com.example.log4u.common.exception.base; + + +import lombok.Getter; + +@Getter +public class ServiceException extends RuntimeException { + + private final ErrorCode errorCode; + + public ServiceException(ErrorCode errorCode) { + super(errorCode.getErrorMessage()); + this.errorCode = errorCode; + } +} From 5dc474fef2d8e7702189d86e7de6a1e4889df715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:22:38 +0900 Subject: [PATCH 10/37] =?UTF-8?q?feat:=20=EA=B3=B5=ED=86=B5=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=9D=91=EB=8B=B5=20=ED=81=B4=EB=9E=98=EC=8A=A4(Ap?= =?UTF-8?q?iErrorResponse)=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B8=B0=EB=B0=98=20=EA=B5=AC=EC=A1=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/ApiErrorResponse.java | 29 ++++++++++++++++ .../common/exception/CommonErrorCode.java | 33 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/main/java/com/example/log4u/common/exception/ApiErrorResponse.java create mode 100644 src/main/java/com/example/log4u/common/exception/CommonErrorCode.java diff --git a/src/main/java/com/example/log4u/common/exception/ApiErrorResponse.java b/src/main/java/com/example/log4u/common/exception/ApiErrorResponse.java new file mode 100644 index 00000000..63afc991 --- /dev/null +++ b/src/main/java/com/example/log4u/common/exception/ApiErrorResponse.java @@ -0,0 +1,29 @@ +package com.example.log4u.common.exception; + +import java.util.List; + +import org.springframework.validation.FieldError; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@Builder +@RequiredArgsConstructor +public class ApiErrorResponse { + private final String errorMessage; + private final int errorCode; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private final List errors; + + public record ValidationError(String field, String message) { + + public static ValidationError of(final FieldError fieldError) { + return new ValidationError(fieldError.getField(), fieldError.getDefaultMessage()); + } + } +} diff --git a/src/main/java/com/example/log4u/common/exception/CommonErrorCode.java b/src/main/java/com/example/log4u/common/exception/CommonErrorCode.java new file mode 100644 index 00000000..bbfa3b01 --- /dev/null +++ b/src/main/java/com/example/log4u/common/exception/CommonErrorCode.java @@ -0,0 +1,33 @@ +package com.example.log4u.common.exception; + +import org.springframework.http.HttpStatus; + +import com.example.log4u.common.exception.base.ErrorCode; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum CommonErrorCode implements ErrorCode { + + INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 요청입니다"), + UNAUTHENTICATED(HttpStatus.UNAUTHORIZED,"로그인이 필요한 기능입니다."), + FORBIDDEN(HttpStatus.FORBIDDEN, "접근 권한이 없습니다"), + RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "요청 정보를 찾을 수 없습니다"), + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다. 관리자에게 문의하세요.") + ; + + private final HttpStatus httpStatus; + private final String message; + + @Override + public HttpStatus getHttpStatus() { + return this.httpStatus; + } + + @Override + public String getErrorMessage() { + return this.message; + } +} From 82b3e2cd5c204f58f2cab9b3bb9b4753cd8b551a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:23:13 +0900 Subject: [PATCH 11/37] =?UTF-8?q?feat:=20=EC=99=B8=EB=B6=80=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExternalApiRequestException.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/com/example/log4u/common/external/exception/ExternalApiRequestException.java diff --git a/src/main/java/com/example/log4u/common/external/exception/ExternalApiRequestException.java b/src/main/java/com/example/log4u/common/external/exception/ExternalApiRequestException.java new file mode 100644 index 00000000..54b0717d --- /dev/null +++ b/src/main/java/com/example/log4u/common/external/exception/ExternalApiRequestException.java @@ -0,0 +1,13 @@ +package com.example.log4u.common.external.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ExternalApiRequestException extends RuntimeException{ + + private final String statusCode; + private final String message; + +} From ac433553c28c23896729cef39dfcd31c5093deca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:23:39 +0900 Subject: [PATCH 12/37] =?UTF-8?q?feat:=20RestTemplate=EC=9A=A9=20ResponseE?= =?UTF-8?q?rrorHandler=20=EB=B0=8F=20=EC=84=A4=EC=A0=95=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log4u/common/external/ClientConfig.java | 18 ++++++++++ .../hanlder/ApiResponseErrorHandler.java | 34 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/main/java/com/example/log4u/common/external/ClientConfig.java create mode 100644 src/main/java/com/example/log4u/common/external/hanlder/ApiResponseErrorHandler.java diff --git a/src/main/java/com/example/log4u/common/external/ClientConfig.java b/src/main/java/com/example/log4u/common/external/ClientConfig.java new file mode 100644 index 00000000..e78d537b --- /dev/null +++ b/src/main/java/com/example/log4u/common/external/ClientConfig.java @@ -0,0 +1,18 @@ +package com.example.log4u.common.external; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import com.example.log4u.common.external.hanlder.ApiResponseErrorHandler; + +@Configuration +public class ClientConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setErrorHandler(new ApiResponseErrorHandler()); + return restTemplate; + } +} diff --git a/src/main/java/com/example/log4u/common/external/hanlder/ApiResponseErrorHandler.java b/src/main/java/com/example/log4u/common/external/hanlder/ApiResponseErrorHandler.java new file mode 100644 index 00000000..4bd2acdd --- /dev/null +++ b/src/main/java/com/example/log4u/common/external/hanlder/ApiResponseErrorHandler.java @@ -0,0 +1,34 @@ +package com.example.log4u.common.external.hanlder; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.stream.Collectors; + +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; + +import com.example.log4u.common.external.exception.ExternalApiRequestException; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ApiResponseErrorHandler implements ResponseErrorHandler { + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return !response.getStatusCode().is2xxSuccessful(); + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + String body; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody()))) { + body = reader.lines().collect(Collectors.joining("\n")); + } + + log.error("API 호출 중 에러 발생: HTTP 상태 코드: {}, 응답 본문: {}", response.getStatusCode().value(), body); + + throw new ExternalApiRequestException(response.getStatusCode().toString(), + "API 호출 중 에러 발생: " + response.getStatusCode().value() + " 응답 본문: " + body); + } +} From 5c50c2d67dc3a2862a819357d0dbaf853e6af132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:24:36 +0900 Subject: [PATCH 13/37] =?UTF-8?q?feat:=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=82=AC=EC=9A=A9=20=EC=98=88=EC=8B=9C?= =?UTF-8?q?=EB=A1=9C=20Comment=20=EB=8F=84=EB=A9=94=EC=9D=B8=20Exception?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EC=98=88=EC=8B=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/exception/CommentErrorCode.java | 28 +++++++++++++ .../comment/exception/CommentException.java | 10 +++++ .../exception/NotFoundCommentException.java | 7 ++++ .../testController/TestController.java | 40 +++++++++++++++++++ .../domain/comment/testDto/TestRequest.java | 19 +++++++++ 5 files changed, 104 insertions(+) create mode 100644 src/main/java/com/example/log4u/domain/comment/exception/CommentErrorCode.java create mode 100644 src/main/java/com/example/log4u/domain/comment/exception/CommentException.java create mode 100644 src/main/java/com/example/log4u/domain/comment/exception/NotFoundCommentException.java create mode 100644 src/main/java/com/example/log4u/domain/comment/testController/TestController.java create mode 100644 src/main/java/com/example/log4u/domain/comment/testDto/TestRequest.java diff --git a/src/main/java/com/example/log4u/domain/comment/exception/CommentErrorCode.java b/src/main/java/com/example/log4u/domain/comment/exception/CommentErrorCode.java new file mode 100644 index 00000000..9b6bf8ed --- /dev/null +++ b/src/main/java/com/example/log4u/domain/comment/exception/CommentErrorCode.java @@ -0,0 +1,28 @@ +package com.example.log4u.domain.comment.exception; + +import org.springframework.http.HttpStatus; + +import com.example.log4u.common.exception.base.ErrorCode; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum CommentErrorCode implements ErrorCode { + + NOT_FOUND_COMMENT(HttpStatus.NOT_FOUND, "댓글을 찾을 수 없습니다."); + + private final HttpStatus httpStatus; + private final String message; + + @Override + public HttpStatus getHttpStatus() { + return httpStatus; + } + + @Override + public String getErrorMessage() { + return message; + } +} diff --git a/src/main/java/com/example/log4u/domain/comment/exception/CommentException.java b/src/main/java/com/example/log4u/domain/comment/exception/CommentException.java new file mode 100644 index 00000000..17b0ee87 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/comment/exception/CommentException.java @@ -0,0 +1,10 @@ +package com.example.log4u.domain.comment.exception; + +import com.example.log4u.common.exception.base.ErrorCode; +import com.example.log4u.common.exception.base.ServiceException; + +public class CommentException extends ServiceException { + public CommentException(ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/com/example/log4u/domain/comment/exception/NotFoundCommentException.java b/src/main/java/com/example/log4u/domain/comment/exception/NotFoundCommentException.java new file mode 100644 index 00000000..af319025 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/comment/exception/NotFoundCommentException.java @@ -0,0 +1,7 @@ +package com.example.log4u.domain.comment.exception; + +public class NotFoundCommentException extends CommentException { + public NotFoundCommentException() { + super(CommentErrorCode.NOT_FOUND_COMMENT); + } +} diff --git a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java new file mode 100644 index 00000000..932ff616 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java @@ -0,0 +1,40 @@ +package com.example.log4u.domain.comment.testController; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.log4u.domain.comment.testDto.TestRequest; +import com.example.log4u.domain.comment.exception.NotFoundCommentException; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/test") +public class TestController { + + @PostMapping("/valid") + public ResponseEntity testValidation(@RequestBody @Valid TestRequest request) { + return ResponseEntity.ok().build(); + } + + @GetMapping("/illegal") + public String testIllegalArgument() { + throw new IllegalArgumentException("잘못된 인자입니다!"); + } + + @GetMapping("/log4u") + public String testLog4uException() { + throw new NotFoundCommentException(); // 또는 임의의 ServiceException + } + + @GetMapping("/unknown") + public String testUnexpectedException() { + String str = null; + str.length(); // NPE + return "절대 도달하지 않음"; + } +} diff --git a/src/main/java/com/example/log4u/domain/comment/testDto/TestRequest.java b/src/main/java/com/example/log4u/domain/comment/testDto/TestRequest.java new file mode 100644 index 00000000..90c051ef --- /dev/null +++ b/src/main/java/com/example/log4u/domain/comment/testDto/TestRequest.java @@ -0,0 +1,19 @@ +package com.example.log4u.domain.comment.testDto; + +// dto/TestRequest.java +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TestRequest { + + @NotBlank + private String name; + + @Min(value = 18, message = "나이는 18세 이상이어야 합니다.") + private int age; + +} From d03d62496e3725eb74da97a1044708d32bf8837c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Tue, 25 Mar 2025 18:25:23 +0900 Subject: [PATCH 14/37] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=9C=20401=20=EC=83=81=ED=83=9C=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EC=8A=A4=ED=94=84=EB=A7=81=20?= =?UTF-8?q?=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0ad7d420..74125b17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,8 +28,8 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") - implementation("org.springframework.boot:spring-boot-starter-oauth2-client") - implementation("org.springframework.boot:spring-boot-starter-security") +// implementation("org.springframework.boot:spring-boot-starter-oauth2-client") +// implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-data-redis") From 08387f50c6c2b5c43288cef5344550190106565b Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 18:45:54 +0900 Subject: [PATCH 15/37] =?UTF-8?q?chore:=20gpt=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 08c2eeb8..7972b2ba 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -9,12 +9,23 @@ on: types: [opened, synchronize] jobs: - test: + review: runs-on: ubuntu-latest steps: - - uses: anc95/ChatGPT-CodeReview@main - env: +# - uses: anc95/ChatGPT-CodeReview@main +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} +# LANGUAGE: Korean +# MODEL: gpt-3.5-turbo + + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: AI Code Reviewer + uses: sapiens2000/ai-code-reviewer@main + with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - LANGUAGE: Korean - MODEL: gpt-3.5-turbo \ No newline at end of file + OPENAI_API_MODEL: "gpt-4" # + exclude: "**/*.json, **/*.md" \ No newline at end of file From 1626c9abede5217dadebe201ae767ebc8b5b7697 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 18:49:01 +0900 Subject: [PATCH 16/37] =?UTF-8?q?chore:=20gpt=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 7972b2ba..3783235d 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -2,7 +2,7 @@ name: Code Review From ChatGPT permissions: contents: read - pull-requests: write + pull-requests: write-all on: pull_request: From 5551a8dd61bc55ff2b96b2c8adf2d2173253bbe0 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 20:23:34 +0900 Subject: [PATCH 17/37] =?UTF-8?q?chore:=20claude=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/code_review.py | 148 +++++++++++++++++++++++ .github/workflows/code-review-claude.yml | 34 ++++++ .github/workflows/code-review-gpt.yml | 21 ++++ .github/workflows/code-review.yml | 31 ----- 4 files changed, 203 insertions(+), 31 deletions(-) create mode 100644 .github/scripts/code_review.py create mode 100644 .github/workflows/code-review-claude.yml create mode 100644 .github/workflows/code-review-gpt.yml delete mode 100644 .github/workflows/code-review.yml diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py new file mode 100644 index 00000000..53166de5 --- /dev/null +++ b/.github/scripts/code_review.py @@ -0,0 +1,148 @@ +import os +import requests +import json +import re +from github import Github +from collections import defaultdict +from concurrent.futures import ThreadPoolExecutor, as_completed + +def get_changed_files(pr): + changed_files = [] + for file in pr.get_files(): + if file.filename.endswith('.java'): + changed_files.append({ + 'filename': file.filename, + 'patch': file.patch, + 'status': file.status, + }) + return changed_files + +def get_file_content(repo, file_path, ref): + return repo.get_contents(file_path, ref=ref).decoded_content.decode('utf-8') + +def search_file(repo, file, changed_files, ref): + if file.type == 'file' and file.name.endswith('.java'): + content = get_file_content(repo, file.path, ref) + related = set() + for changed_file in changed_files: + changed_name = os.path.splitext(os.path.basename(changed_file['filename']))[0] + if re.search(r'\b' + re.escape(changed_name) + r'\b', content): + related.add(changed_file['filename']) + return file.path, related + return None, set() + +def find_related_files(repo, changed_files, ref): + related_files = defaultdict(set) + all_files = repo.get_contents('', ref=ref) + dirs_to_process = [file for file in all_files if file.type == 'dir'] + + with ThreadPoolExecutor(max_workers=10) as executor: + future_to_file = {executor.submit(search_file, repo, file, changed_files, ref): file for file in all_files if file.type == 'file'} + + while dirs_to_process: + dir_files = repo.get_contents(dirs_to_process.pop().path, ref=ref) + dirs_to_process.extend([file for file in dir_files if file.type == 'dir']) + future_to_file.update({executor.submit(search_file, repo, file, changed_files, ref): file for file in dir_files if file.type == 'file'}) + + for future in as_completed(future_to_file): + file_path, related = future.result() + if related: + for changed_file in related: + related_files[changed_file].add(file_path) + + return related_files + + +def call_claude_api(changes, related_files): + url = "https://api.anthropic.com/v1/messages" + headers = { + "Content-Type": "application/json", + "x-api-key": os.environ['CLAUDE_API_KEY'], + "anthropic-version": "2023-06-01" + } + + system_content = ( + "경험 많은 시니어 개발자로서, 다음 변경사항들에 대해 전체적이고 간결한 코드 리뷰를 수행해주세요.\n\n" + "리뷰 지침:\n" + "1. 모든 변경사항을 종합적으로 검토하고, 가장 중요한 문제점이나 개선사항에만 집중하세요.\n" + "2. 파일별로 개별 리뷰를 하지 말고, 전체 변경사항에 대한 통합된 리뷰를 제공하세요.\n" + "3. 각 주요 이슈에 대해 간단한 설명과 구체적인 개선 제안을 제시하세요.\n" + "4. 개선 제안에는 실제 코드 예시를 포함하세요. 단, 코드 예시는 제공한 코드와 연관된 코드여야 합니다. \n" + "5. 사소한 스타일 문제나 개인적 선호도는 무시하세요.\n" + "6. 심각한 버그, 성능 문제, 또는 보안 취약점이 있는 경우에만 언급하세요.\n" + "7. 전체 리뷰는 간결하게 유지하세요.\n" + "8. 변경된 부분만 집중하여 리뷰하고, 이미 개선된 코드를 다시 지적하지 마세요.\n" + "9. 기존에 이미 개선된 사항(예: 중복 코드 제거를 위한 함수 생성)을 인식하고 이를 긍정적으로 언급하세요.\n" + "10. 변경된 파일과 관련된 다른 파일들에 미칠 수 있는 영향을 분석하세요.\n\n" + "리뷰 형식:\n" + "- 개선된 사항: [이미 개선된 부분에 대한 긍정적 언급]\n" + "- 주요 이슈 (있는 경우에만):\n" + " 1. [문제 설명]\n" + " - 제안: [개선 방안 설명]\n" + " ```typescript\n" + " // 수정된 코드 예시\n" + " ```\n" + " 2. ...\n" + "- 관련 파일에 대한 영향 분석:\n" + " [변경된 파일과 관련된 다른 파일들에 미칠 수 있는 잠재적 영향 설명]\n" + "- 전반적인 의견: [1-2문장으로 요약]\n\n" + "변경된 파일들:\n" + ) + + for file_info in changes: + system_content += f"- {file_info['filename']} ({file_info['status']})\n" + + system_content += "\n변경 내용:\n" + for file_info in changes: + system_content += f"파일: {file_info['filename']}\n전체 내용:\n{file_info['full_content']}\n\n변경된 부분:\n{file_info['patch']}\n\n" + + system_content += "\n관련된 파일들:\n" + for changed_file, related in related_files.items(): + system_content += f"- {changed_file}에 영향을 받을 수 있는 파일들:\n" + for related_file in related: + system_content += f" - {related_file}\n" + + payload = { + "model": "claude-3-5-sonnet-20240620", + "max_tokens": 2000, + "system": system_content, + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "제공된 모든 변경사항에 대해 통합된, 간결하고 핵심적인 코드 리뷰를 제공해주세요. 가장 중요한 이슈에만 집중하고, 각 개선 제안에는 구체적인 코드 예시를 포함해주세요. 변경된 부분만 집중하여 리뷰하고, 이미 개선된 코드를 다시 지적하지 마세요. 또한, 변경된 파일과 관련된 다른 파일들에 미칠 수 있는 잠재적 영향을 분석해주세요." + } + ] + } + ] + } + + response = requests.post(url, headers=headers, json=payload) + if response.status_code == 200: + return response.json()['content'][0]['text'] + else: + return f"Error: API returned status code {response.status_code}" + +def main(): + g = Github(os.environ['GITHUB_TOKEN']) + repo = g.get_repo(os.environ['GITHUB_REPOSITORY']) + pr_number = int(os.environ['PR_NUMBER']) + pr = repo.get_pull(pr_number) + + changed_files = get_changed_files(pr) + changes = [] + + for file_info in changed_files: + full_content = get_file_content(repo, file_info['filename'], pr.head.sha) + file_info['full_content'] = full_content + changes.append(file_info) + + related_files = find_related_files(repo, changed_files, pr.head.sha) + review = call_claude_api(changes, related_files) + + pr.create_issue_comment(f"Claude의 전체 변경사항 및 관련 파일에 대한 리뷰:\n\n{review}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.github/workflows/code-review-claude.yml b/.github/workflows/code-review-claude.yml new file mode 100644 index 00000000..819e2cb0 --- /dev/null +++ b/.github/workflows/code-review-claude.yml @@ -0,0 +1,34 @@ +name: Code Review from Claude + +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: read + pull-requests: write + +jobs: + review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install requests PyGithub + + - name: Run Code Review + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: python .github/scripts/code_review.py \ No newline at end of file diff --git a/.github/workflows/code-review-gpt.yml b/.github/workflows/code-review-gpt.yml new file mode 100644 index 00000000..dc24f608 --- /dev/null +++ b/.github/workflows/code-review-gpt.yml @@ -0,0 +1,21 @@ +name: Code Review From ChatGPT + +permissions: + contents: read + pull-requests: write + +on: + pull_request: + types: [opened, synchronize] + +jobs: + review: + runs-on: ubuntu-latest + steps: + - uses: anc95/ChatGPT-CodeReview@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LANGUAGE: Korean + MODEL: gpt-3.5-turbo + diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml deleted file mode 100644 index 3783235d..00000000 --- a/.github/workflows/code-review.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Code Review From ChatGPT - -permissions: - contents: read - pull-requests: write-all - -on: - pull_request: - types: [opened, synchronize] - -jobs: - review: - runs-on: ubuntu-latest - steps: -# - uses: anc95/ChatGPT-CodeReview@main -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} -# LANGUAGE: Korean -# MODEL: gpt-3.5-turbo - - - name: Checkout Repo - uses: actions/checkout@v3 - - - name: AI Code Reviewer - uses: sapiens2000/ai-code-reviewer@main - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - OPENAI_API_MODEL: "gpt-4" # - exclude: "**/*.json, **/*.md" \ No newline at end of file From e3bc7c1d0e977a1c4e4a24cc8ab7c778e98489c0 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 21:26:08 +0900 Subject: [PATCH 18/37] =?UTF-8?q?chore:=20ai=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EB=8D=94=EB=AF=B8=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review-gpt.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/code-review-gpt.yml b/.github/workflows/code-review-gpt.yml index dc24f608..5e2d6540 100644 --- a/.github/workflows/code-review-gpt.yml +++ b/.github/workflows/code-review-gpt.yml @@ -18,4 +18,3 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} LANGUAGE: Korean MODEL: gpt-3.5-turbo - From d99177351def190c826e6203efd886c5ba0ceca2 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Tue, 25 Mar 2025 22:49:34 +0900 Subject: [PATCH 19/37] =?UTF-8?q?chore:=20=EC=9E=90=EB=B0=94=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A7=8C=20=20=EB=A6=AC=EB=B7=B0=20=ED=95=98=EA=B2=8C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/code_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 53166de5..79ac173d 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -79,7 +79,7 @@ def call_claude_api(changes, related_files): "- 주요 이슈 (있는 경우에만):\n" " 1. [문제 설명]\n" " - 제안: [개선 방안 설명]\n" - " ```typescript\n" + " ```java\n" " // 수정된 코드 예시\n" " ```\n" " 2. ...\n" From 84b9643bc1542050e85162e768d48317651dda57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:25:07 +0900 Subject: [PATCH 20/37] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20API?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=B4=20Diary=20domain=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=95=84=EC=9A=94=ED=95=9C=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4,=20Entity=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log4u/domain/diary/entity/Diary.java | 53 +++++++++++++++++++ .../diary/exception/DiaryException.java | 9 ++++ .../exception/NotFoundDiaryException.java | 4 ++ .../diary/repository/DiaryRepository.java | 9 ++++ .../domain/diary/service/DiaryService.java | 26 +++++++++ 5 files changed, 101 insertions(+) create mode 100644 src/main/java/com/example/log4u/domain/diary/entity/Diary.java create mode 100644 src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java create mode 100644 src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java create mode 100644 src/main/java/com/example/log4u/domain/diary/repository/DiaryRepository.java create mode 100644 src/main/java/com/example/log4u/domain/diary/service/DiaryService.java diff --git a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java new file mode 100644 index 00000000..860dc61e --- /dev/null +++ b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java @@ -0,0 +1,53 @@ +package com.example.log4u.domain.diary.entity; + +import com.example.log4u.domain.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +public class Diary extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long diaryId; + + //JPA 연관관계 사용 X + // 외래키 방식을 사용 O + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + private String title; + + private String thumbnailUrl; + + @Column(nullable = false) + private String content; + + @Column(nullable = false) + private Double latitude; + + @Column(nullable = false) + private Double longitude; + + @Column(nullable = false) + private Long likeCount; + + public Long updateLikeCount() { + this.likeCount++; + return this.likeCount; + } +} diff --git a/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java b/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java new file mode 100644 index 00000000..c29af581 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java @@ -0,0 +1,9 @@ +// package com.example.log4u.domain.diary.exception; +// +// public class DiaryException extends ServiceException{ +// +// DiaryException(ErrorCode errorCode){ +// super(errorCode); +// } +// +// } diff --git a/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java b/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java new file mode 100644 index 00000000..65c4ee65 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java @@ -0,0 +1,4 @@ +// package com.example.log4u.domain.diary.exception; +// +// public class NotFoundDiaryException extends DiaryException{ +// } diff --git a/src/main/java/com/example/log4u/domain/diary/repository/DiaryRepository.java b/src/main/java/com/example/log4u/domain/diary/repository/DiaryRepository.java new file mode 100644 index 00000000..fbd9d14b --- /dev/null +++ b/src/main/java/com/example/log4u/domain/diary/repository/DiaryRepository.java @@ -0,0 +1,9 @@ +package com.example.log4u.domain.diary.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.log4u.domain.diary.entity.Diary; + +public interface DiaryRepository extends JpaRepository +{ +} diff --git a/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java b/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java new file mode 100644 index 00000000..96270c05 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java @@ -0,0 +1,26 @@ +package com.example.log4u.domain.diary.service; + +import org.springframework.stereotype.Service; + +import com.example.log4u.domain.diary.entity.Diary; +import com.example.log4u.domain.diary.repository.DiaryRepository; + +import lombok.RequiredArgsConstructor; + + +@Service +@RequiredArgsConstructor +public class DiaryService { + + private final DiaryRepository diaryRepository; + + public Diary getDiary(Long diaryId) { + return diaryRepository.findById(diaryId) + .orElseThrow(IllegalArgumentException::new); + } + + public Long incrementLikeCount(Long diaryId) { + Diary diary = getDiary(diaryId); + return diary.updateLikeCount(); + } +} From a186c06d832b653e7a8d11a257596a6fe3085f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:25:52 +0900 Subject: [PATCH 21/37] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20API?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=B4=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?User=20domain=20=EA=B4=80=EB=A0=A8=20Entity=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log4u/domain/user/entity/SocialType.java | 7 +++ .../log4u/domain/user/entity/User.java | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/main/java/com/example/log4u/domain/user/entity/SocialType.java create mode 100644 src/main/java/com/example/log4u/domain/user/entity/User.java diff --git a/src/main/java/com/example/log4u/domain/user/entity/SocialType.java b/src/main/java/com/example/log4u/domain/user/entity/SocialType.java new file mode 100644 index 00000000..84f2002f --- /dev/null +++ b/src/main/java/com/example/log4u/domain/user/entity/SocialType.java @@ -0,0 +1,7 @@ +package com.example.log4u.domain.user.entity; + +public enum SocialType { + KAKAO, + GOOGLE, + NAVER, +} diff --git a/src/main/java/com/example/log4u/domain/user/entity/User.java b/src/main/java/com/example/log4u/domain/user/entity/User.java new file mode 100644 index 00000000..a70ffe07 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/user/entity/User.java @@ -0,0 +1,46 @@ +package com.example.log4u.domain.user.entity; + +import com.example.log4u.domain.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +public class User extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long userId; + + @Column(nullable = false) + private String nickname; + + @Column(nullable = false) + private Long providerId; + + @Column(nullable = false) + private String email; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private SocialType socialType; + + private String statusMessage; + + @Column(nullable = false) + private boolean isPremium; +} From daebe25045a3fe509a82030aebbfc955af0a9246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:26:34 +0900 Subject: [PATCH 22/37] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../like/controller/LikeController.java | 32 ++++++++++++++++ .../like/dto/request/LikeAddRequestDto.java | 16 ++++++++ .../like/dto/response/LikeAddResponseDto.java | 10 +++++ .../log4u/domain/like/entity/Like.java | 34 +++++++++++++++++ .../like/repository/LikeRepository.java | 9 +++++ .../domain/like/service/LikeService.java | 37 +++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 src/main/java/com/example/log4u/domain/like/controller/LikeController.java create mode 100644 src/main/java/com/example/log4u/domain/like/dto/request/LikeAddRequestDto.java create mode 100644 src/main/java/com/example/log4u/domain/like/dto/response/LikeAddResponseDto.java create mode 100644 src/main/java/com/example/log4u/domain/like/entity/Like.java create mode 100644 src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java create mode 100644 src/main/java/com/example/log4u/domain/like/service/LikeService.java diff --git a/src/main/java/com/example/log4u/domain/like/controller/LikeController.java b/src/main/java/com/example/log4u/domain/like/controller/LikeController.java new file mode 100644 index 00000000..7b5d7e3d --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/controller/LikeController.java @@ -0,0 +1,32 @@ +package com.example.log4u.domain.like.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; +import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; +import com.example.log4u.domain.like.service.LikeService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@Tag(name = "좋아요 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/likes") +public class LikeController { + + private final LikeService likeService; + + @PostMapping + public ResponseEntity addLike(@Valid @RequestBody LikeAddRequestDto requestDto) { + Long userId = 1L; // 실제 구현에서는 토큰에서 추출 + + LikeAddResponseDto response = likeService.addLike(userId, requestDto); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/example/log4u/domain/like/dto/request/LikeAddRequestDto.java b/src/main/java/com/example/log4u/domain/like/dto/request/LikeAddRequestDto.java new file mode 100644 index 00000000..e38ef6f6 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/dto/request/LikeAddRequestDto.java @@ -0,0 +1,16 @@ +package com.example.log4u.domain.like.dto.request; + +import com.example.log4u.domain.like.entity.Like; + +public record LikeAddRequestDto( + Long diaryId + +) { + + public Like toEntity(Long userId) { + return Like.builder() + .userId(userId) + .diaryId(diaryId) + .build(); + } +} diff --git a/src/main/java/com/example/log4u/domain/like/dto/response/LikeAddResponseDto.java b/src/main/java/com/example/log4u/domain/like/dto/response/LikeAddResponseDto.java new file mode 100644 index 00000000..6d040813 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/dto/response/LikeAddResponseDto.java @@ -0,0 +1,10 @@ +package com.example.log4u.domain.like.dto.response; + +public record LikeAddResponseDto( + boolean liked, + Long likeCount) { + + public static LikeAddResponseDto of(boolean liked, Long likeCount) { + return new LikeAddResponseDto(liked, likeCount); + } +} diff --git a/src/main/java/com/example/log4u/domain/like/entity/Like.java b/src/main/java/com/example/log4u/domain/like/entity/Like.java new file mode 100644 index 00000000..4b7940dc --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/entity/Like.java @@ -0,0 +1,34 @@ +package com.example.log4u.domain.like.entity; + +import com.example.log4u.domain.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +@Table(name = "likes") +public class Like extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long likeId; + + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + private Long diaryId; +} diff --git a/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java b/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java new file mode 100644 index 00000000..8b521de8 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java @@ -0,0 +1,9 @@ +package com.example.log4u.domain.like.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.log4u.domain.like.entity.Like; + +public interface LikeRepository extends JpaRepository { + boolean existsByUserIdAndDiaryId(Long userId, Long diaryId); +} diff --git a/src/main/java/com/example/log4u/domain/like/service/LikeService.java b/src/main/java/com/example/log4u/domain/like/service/LikeService.java new file mode 100644 index 00000000..4efe6d63 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/service/LikeService.java @@ -0,0 +1,37 @@ +package com.example.log4u.domain.like.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.example.log4u.domain.diary.service.DiaryService; +import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; +import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; +import com.example.log4u.domain.like.entity.Like; +import com.example.log4u.domain.like.repository.LikeRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class LikeService { + + private final LikeRepository likeRepository; + private final DiaryService diaryService; + + @Transactional + public LikeAddResponseDto addLike(Long userId, LikeAddRequestDto requestDto) { + validateDuplicateLike(userId, requestDto.diaryId()); + + Like like = requestDto.toEntity(userId); + likeRepository.save(like); + + Long likeCount = diaryService.incrementLikeCount(requestDto.diaryId()); + return LikeAddResponseDto.of(true, likeCount); + } + + private void validateDuplicateLike(Long userId, Long diaryId) { + if (likeRepository.existsByUserIdAndDiaryId(userId, diaryId)) { + throw new IllegalArgumentException(); + } + } +} From d4e9d39bb7e19225dc56be4e1ff1f206fc2a1ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:29:38 +0900 Subject: [PATCH 23/37] =?UTF-8?q?feat:=20BaseEntity=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EB=B0=8F=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20@EnableJpaAuditing=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/log4u/Log4UApplication.java | 2 ++ .../domain/common/entity/BaseEntity.java | 28 +++++++++++++++++++ .../log4u/domain/diary/entity/Diary.java | 2 +- .../log4u/domain/like/entity/Like.java | 2 +- .../log4u/domain/user/entity/User.java | 2 +- 5 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java diff --git a/src/main/java/com/example/log4u/Log4UApplication.java b/src/main/java/com/example/log4u/Log4UApplication.java index 57fa72f5..d54d143b 100644 --- a/src/main/java/com/example/log4u/Log4UApplication.java +++ b/src/main/java/com/example/log4u/Log4UApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class Log4UApplication { diff --git a/src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java b/src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java new file mode 100644 index 00000000..f07280c0 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java @@ -0,0 +1,28 @@ +package com.example.log4u.domain.common.entity; + +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +abstract public class BaseEntity { + + @CreatedDate + @Column(nullable = false, updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(nullable = false) + private LocalDateTime updatedAt; + + private String deleteYn = "N"; +} diff --git a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java index 860dc61e..8a49cac1 100644 --- a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java +++ b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java @@ -1,6 +1,6 @@ package com.example.log4u.domain.diary.entity; -import com.example.log4u.domain.entity.BaseEntity; +import com.example.log4u.domain.common.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/example/log4u/domain/like/entity/Like.java b/src/main/java/com/example/log4u/domain/like/entity/Like.java index 4b7940dc..97e0d61e 100644 --- a/src/main/java/com/example/log4u/domain/like/entity/Like.java +++ b/src/main/java/com/example/log4u/domain/like/entity/Like.java @@ -1,6 +1,6 @@ package com.example.log4u.domain.like.entity; -import com.example.log4u.domain.entity.BaseEntity; +import com.example.log4u.domain.common.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/example/log4u/domain/user/entity/User.java b/src/main/java/com/example/log4u/domain/user/entity/User.java index a70ffe07..1a450962 100644 --- a/src/main/java/com/example/log4u/domain/user/entity/User.java +++ b/src/main/java/com/example/log4u/domain/user/entity/User.java @@ -1,6 +1,6 @@ package com.example.log4u.domain.user.entity; -import com.example.log4u.domain.entity.BaseEntity; +import com.example.log4u.domain.common.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; From 5a362f6c4674035437d3ff3eac95e745b79a8b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:30:25 +0900 Subject: [PATCH 24/37] =?UTF-8?q?test:=20=EB=8B=A8=EC=9C=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9C=84=ED=95=9C=20Domain=20=EB=B3=84=20?= =?UTF-8?q?Fixture=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/log4u/fixture/DiaryFixture.java | 19 ++++++++ .../example/log4u/fixture/LikeFixture.java | 14 ++++++ .../example/log4u/fixture/UserFixture.java | 44 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 src/test/java/com/example/log4u/fixture/DiaryFixture.java create mode 100644 src/test/java/com/example/log4u/fixture/LikeFixture.java create mode 100644 src/test/java/com/example/log4u/fixture/UserFixture.java diff --git a/src/test/java/com/example/log4u/fixture/DiaryFixture.java b/src/test/java/com/example/log4u/fixture/DiaryFixture.java new file mode 100644 index 00000000..c53dbbe7 --- /dev/null +++ b/src/test/java/com/example/log4u/fixture/DiaryFixture.java @@ -0,0 +1,19 @@ +package com.example.log4u.fixture; + +import com.example.log4u.domain.diary.entity.Diary; + +public class DiaryFixture { + + public static Diary createDiaryFixture() { + return Diary.builder() + .diaryId(1L) + .userId(1L) + .title("테스트 다이어리") + .thumbnailUrl("thumbnail.jpg") + .content("다이어리 내용입니다.") + .latitude(37.1234) + .longitude(127.5678) + .likeCount(11L) + .build(); + } +} diff --git a/src/test/java/com/example/log4u/fixture/LikeFixture.java b/src/test/java/com/example/log4u/fixture/LikeFixture.java new file mode 100644 index 00000000..b57481e8 --- /dev/null +++ b/src/test/java/com/example/log4u/fixture/LikeFixture.java @@ -0,0 +1,14 @@ +package com.example.log4u.fixture; + +import com.example.log4u.domain.like.entity.Like; + +public class LikeFixture { + + public static Like createLikeFixture(Long likeId, Long userId, Long diaryId) { + return Like.builder() + .likeId(likeId) + .userId(userId) + .diaryId(diaryId) + .build(); + } +} diff --git a/src/test/java/com/example/log4u/fixture/UserFixture.java b/src/test/java/com/example/log4u/fixture/UserFixture.java new file mode 100644 index 00000000..db87a26f --- /dev/null +++ b/src/test/java/com/example/log4u/fixture/UserFixture.java @@ -0,0 +1,44 @@ +package com.example.log4u.fixture; + +import com.example.log4u.domain.user.entity.SocialType; +import com.example.log4u.domain.user.entity.User; + +public class UserFixture { + + public static User createUserFixture() { + return User.builder() + .userId(1L) + .nickname("testUser") + .providerId(123L) + .email("test@example.com") + .socialType(SocialType.KAKAO) + .statusMessage("상태 메시지") + .isPremium(false) + .build(); + } + + public static User createUserFixture(Long userId) { + return User.builder() + .userId(userId) + .nickname("testUser" + userId) + .providerId(100L + userId) + .email("test" + userId + "@example.com") + .socialType(SocialType.KAKAO) + .statusMessage("상태 메시지 " + userId) + .isPremium(false) + .build(); + } + + public static User createPremiumUserFixture(Long userId) { + return User.builder() + .userId(userId) + .nickname("premiumUser" + userId) + .providerId(1000L + userId) + .email("premium" + userId + "@example.com") + .socialType(SocialType.KAKAO) + .statusMessage("프리미엄 사용자") + .isPremium(true) + .build(); + } +} + From e6bd210061b4a744c45e25b59676401b282a9837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:30:48 +0900 Subject: [PATCH 25/37] =?UTF-8?q?test:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20API=20=EB=8B=A8=EC=9C=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/like/service/LikeServiceTest.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java diff --git a/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java b/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java new file mode 100644 index 00000000..1152cc17 --- /dev/null +++ b/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java @@ -0,0 +1,98 @@ +package com.example.log4u.domain.like.service; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.example.log4u.domain.diary.entity.Diary; +import com.example.log4u.domain.diary.service.DiaryService; +import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; +import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; +import com.example.log4u.domain.like.entity.Like; +import com.example.log4u.domain.like.repository.LikeRepository; +import com.example.log4u.domain.user.entity.User; +import com.example.log4u.fixture.DiaryFixture; +import com.example.log4u.fixture.LikeFixture; +import com.example.log4u.fixture.UserFixture; + +@DisplayName("좋아요 API 단위 테스트") +@ExtendWith(MockitoExtension.class) +public class LikeServiceTest { + + @InjectMocks + private LikeService likeService; + + @Mock + private LikeRepository likeRepository; + + @Mock + private DiaryService diaryService; + + @Test + @DisplayName("성공 테스트: 사용자가 게시물에 좋아요를 누르면 좋아요가 저장된다") + void likeSuccess() { + // given + User user = UserFixture.createUserFixture(); + Diary diary = DiaryFixture.createDiaryFixture(); + LikeAddRequestDto requestDto = new LikeAddRequestDto(diary.getDiaryId()); + + Like like = LikeFixture.createLikeFixture(123243L, user.getUserId(), diary.getDiaryId()); + Long updatedLikeCount = 11L; + + given(likeRepository.existsByUserIdAndDiaryId(user.getUserId(), diary.getDiaryId())).willReturn(false); + given(likeRepository.save(any(Like.class))).willReturn(like); + given(diaryService.incrementLikeCount(diary.getDiaryId())).willReturn(updatedLikeCount); + + // when + LikeAddResponseDto response = likeService.addLike(user.getUserId(), requestDto); + + // then + verify(likeRepository).save(any(Like.class)); + assertThat(response.liked()).isTrue(); + assertThat(response.likeCount()).isEqualTo(updatedLikeCount); + } + + @Test + @DisplayName("예외 테스트: 존재하지 않는 다이어리에 좋아요 요청 시 예외가 발생한다") + void likeFail_whenDiaryNotFound() { + // given + Long userId = 1L; + Long diaryId = 100L; + LikeAddRequestDto requestDto = new LikeAddRequestDto(diaryId); + + given(likeRepository.existsByUserIdAndDiaryId(userId, diaryId)).willReturn(false); + given(diaryService.incrementLikeCount(diaryId)).willThrow(new IllegalArgumentException()); + + // when & then + assertThrows(IllegalArgumentException.class, () -> { + likeService.addLike(userId, requestDto); + }); + + verify(likeRepository).save(any(Like.class)); + } + + @Test + @DisplayName("예외 테스트: 사용자가 이미 좋아요를 누른 다이어리에 또 요청하면 예외가 발생한다") + void likeFail_whenAlreadyLiked() { + // given + Long userId = 1L; + Long diaryId = 100L; + LikeAddRequestDto requestDto = new LikeAddRequestDto(diaryId); + + given(likeRepository.existsByUserIdAndDiaryId(userId, diaryId)).willReturn(true); + + // when & then + assertThrows(IllegalArgumentException.class, () -> { + likeService.addLike(userId, requestDto); + }); + + verify(likeRepository, never()).save(any(Like.class)); + } +} From 5e68a39b3b6bb287b25e824a7b78aeae3a1d85f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:31:47 +0900 Subject: [PATCH 26/37] =?UTF-8?q?build:=20Security=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=9E=84=EC=8B=9C=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20mysql=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구현 후 다시 변경 예정 --- build.gradle.kts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0ad7d420..db887474 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,13 +28,16 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") - implementation("org.springframework.boot:spring-boot-starter-oauth2-client") - implementation("org.springframework.boot:spring-boot-starter-security") +// implementation("org.springframework.boot:spring-boot-starter-oauth2-client") +// implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-data-redis") + implementation("mysql:mysql-connector-java:8.0.33") compileOnly("org.projectlombok:lombok") + testCompileOnly("org.projectlombok:lombok") + testAnnotationProcessor("org.projectlombok:lombok") runtimeOnly("com.h2database:h2") annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-test") From 7ea9ed1730097fc0c0d8b7455e331b2645ea8e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:43:40 +0900 Subject: [PATCH 27/37] =?UTF-8?q?rename:=20BaseEntity=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/log4u/{domain => }/common/entity/BaseEntity.java | 2 +- src/main/java/com/example/log4u/domain/diary/entity/Diary.java | 2 +- src/main/java/com/example/log4u/domain/like/entity/Like.java | 2 +- src/main/java/com/example/log4u/domain/user/entity/User.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/com/example/log4u/{domain => }/common/entity/BaseEntity.java (93%) diff --git a/src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java b/src/main/java/com/example/log4u/common/entity/BaseEntity.java similarity index 93% rename from src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java rename to src/main/java/com/example/log4u/common/entity/BaseEntity.java index f07280c0..6f5adb62 100644 --- a/src/main/java/com/example/log4u/domain/common/entity/BaseEntity.java +++ b/src/main/java/com/example/log4u/common/entity/BaseEntity.java @@ -1,4 +1,4 @@ -package com.example.log4u.domain.common.entity; +package com.example.log4u.common.entity; import java.time.LocalDateTime; diff --git a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java index 8a49cac1..a3cbadcb 100644 --- a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java +++ b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java @@ -1,6 +1,6 @@ package com.example.log4u.domain.diary.entity; -import com.example.log4u.domain.common.entity.BaseEntity; +import com.example.log4u.common.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/example/log4u/domain/like/entity/Like.java b/src/main/java/com/example/log4u/domain/like/entity/Like.java index 97e0d61e..6b449d4e 100644 --- a/src/main/java/com/example/log4u/domain/like/entity/Like.java +++ b/src/main/java/com/example/log4u/domain/like/entity/Like.java @@ -1,6 +1,6 @@ package com.example.log4u.domain.like.entity; -import com.example.log4u.domain.common.entity.BaseEntity; +import com.example.log4u.common.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/example/log4u/domain/user/entity/User.java b/src/main/java/com/example/log4u/domain/user/entity/User.java index 1a450962..b20e0d9d 100644 --- a/src/main/java/com/example/log4u/domain/user/entity/User.java +++ b/src/main/java/com/example/log4u/domain/user/entity/User.java @@ -1,6 +1,6 @@ package com.example.log4u.domain.user.entity; -import com.example.log4u.domain.common.entity.BaseEntity; +import com.example.log4u.common.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; From 490b3770acaea404c8756128771e99bdc94f66bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:44:26 +0900 Subject: [PATCH 28/37] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20=EA=B0=81=20Domain=EC=97=90=20?= =?UTF-8?q?=EC=95=8C=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../diary/exception/DiaryErrorCode.java | 29 +++++++++++++++++ .../diary/exception/DiaryException.java | 20 ++++++------ .../exception/NotFoundDiaryException.java | 11 ++++--- .../domain/diary/service/DiaryService.java | 3 +- .../exception/DuplicateLikeException.java | 7 +++++ .../domain/like/exception/LikeErrorCode.java | 31 +++++++++++++++++++ .../domain/like/exception/LikeException.java | 10 ++++++ .../domain/like/service/LikeService.java | 3 +- 8 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/example/log4u/domain/diary/exception/DiaryErrorCode.java create mode 100644 src/main/java/com/example/log4u/domain/like/exception/DuplicateLikeException.java create mode 100644 src/main/java/com/example/log4u/domain/like/exception/LikeErrorCode.java create mode 100644 src/main/java/com/example/log4u/domain/like/exception/LikeException.java diff --git a/src/main/java/com/example/log4u/domain/diary/exception/DiaryErrorCode.java b/src/main/java/com/example/log4u/domain/diary/exception/DiaryErrorCode.java new file mode 100644 index 00000000..def651be --- /dev/null +++ b/src/main/java/com/example/log4u/domain/diary/exception/DiaryErrorCode.java @@ -0,0 +1,29 @@ +package com.example.log4u.domain.diary.exception; + +import org.springframework.http.HttpStatus; + +import com.example.log4u.common.exception.base.ErrorCode; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum DiaryErrorCode implements ErrorCode { + + NOT_FOUND_DIARY(HttpStatus.NOT_FOUND, "다이어리를 찾을 수 없습니다."); + + private final HttpStatus httpStatus; + private final String message; + + @Override + public HttpStatus getHttpStatus() { + return httpStatus; + } + + @Override + public String getErrorMessage() { + return message; + } +} + diff --git a/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java b/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java index c29af581..b8f325f8 100644 --- a/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java +++ b/src/main/java/com/example/log4u/domain/diary/exception/DiaryException.java @@ -1,9 +1,11 @@ -// package com.example.log4u.domain.diary.exception; -// -// public class DiaryException extends ServiceException{ -// -// DiaryException(ErrorCode errorCode){ -// super(errorCode); -// } -// -// } +package com.example.log4u.domain.diary.exception; + +import com.example.log4u.common.exception.base.ErrorCode; +import com.example.log4u.common.exception.base.ServiceException; + +public class DiaryException extends ServiceException { + public DiaryException(ErrorCode errorCode) { + super(errorCode); + } +} + diff --git a/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java b/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java index 65c4ee65..452f1141 100644 --- a/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java +++ b/src/main/java/com/example/log4u/domain/diary/exception/NotFoundDiaryException.java @@ -1,4 +1,7 @@ -// package com.example.log4u.domain.diary.exception; -// -// public class NotFoundDiaryException extends DiaryException{ -// } +package com.example.log4u.domain.diary.exception; + +public class NotFoundDiaryException extends DiaryException { + public NotFoundDiaryException() { + super(DiaryErrorCode.NOT_FOUND_DIARY); + } +} diff --git a/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java b/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java index 96270c05..f3755e51 100644 --- a/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java +++ b/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Service; import com.example.log4u.domain.diary.entity.Diary; +import com.example.log4u.domain.diary.exception.NotFoundDiaryException; import com.example.log4u.domain.diary.repository.DiaryRepository; import lombok.RequiredArgsConstructor; @@ -16,7 +17,7 @@ public class DiaryService { public Diary getDiary(Long diaryId) { return diaryRepository.findById(diaryId) - .orElseThrow(IllegalArgumentException::new); + .orElseThrow(NotFoundDiaryException::new); } public Long incrementLikeCount(Long diaryId) { diff --git a/src/main/java/com/example/log4u/domain/like/exception/DuplicateLikeException.java b/src/main/java/com/example/log4u/domain/like/exception/DuplicateLikeException.java new file mode 100644 index 00000000..46096185 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/exception/DuplicateLikeException.java @@ -0,0 +1,7 @@ +package com.example.log4u.domain.like.exception; + +public class DuplicateLikeException extends LikeException { + public DuplicateLikeException() { + super(LikeErrorCode.DUPLICATE_LIKE); + } +} diff --git a/src/main/java/com/example/log4u/domain/like/exception/LikeErrorCode.java b/src/main/java/com/example/log4u/domain/like/exception/LikeErrorCode.java new file mode 100644 index 00000000..bd11f6d6 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/exception/LikeErrorCode.java @@ -0,0 +1,31 @@ +package com.example.log4u.domain.like.exception; + +import org.springframework.http.HttpStatus; + +import com.example.log4u.common.exception.base.ErrorCode; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum LikeErrorCode implements ErrorCode { + + NOT_FOUND_LIKE(HttpStatus.NOT_FOUND, "좋아요 정보를 찾을 수 없습니다."), + DUPLICATE_LIKE(HttpStatus.BAD_REQUEST, "이미 좋아요를 눌렀습니다."); + + + private final HttpStatus httpStatus; + private final String message; + + @Override + public HttpStatus getHttpStatus() { + return httpStatus; + } + + @Override + public String getErrorMessage() { + return message; + } +} + diff --git a/src/main/java/com/example/log4u/domain/like/exception/LikeException.java b/src/main/java/com/example/log4u/domain/like/exception/LikeException.java new file mode 100644 index 00000000..ffca40df --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/exception/LikeException.java @@ -0,0 +1,10 @@ +package com.example.log4u.domain.like.exception; + +import com.example.log4u.common.exception.base.ErrorCode; +import com.example.log4u.common.exception.base.ServiceException; + +public class LikeException extends ServiceException { + public LikeException(ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/com/example/log4u/domain/like/service/LikeService.java b/src/main/java/com/example/log4u/domain/like/service/LikeService.java index 4efe6d63..a48d7d2f 100644 --- a/src/main/java/com/example/log4u/domain/like/service/LikeService.java +++ b/src/main/java/com/example/log4u/domain/like/service/LikeService.java @@ -7,6 +7,7 @@ import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; import com.example.log4u.domain.like.entity.Like; +import com.example.log4u.domain.like.exception.DuplicateLikeException; import com.example.log4u.domain.like.repository.LikeRepository; import lombok.RequiredArgsConstructor; @@ -31,7 +32,7 @@ public LikeAddResponseDto addLike(Long userId, LikeAddRequestDto requestDto) { private void validateDuplicateLike(Long userId, Long diaryId) { if (likeRepository.existsByUserIdAndDiaryId(userId, diaryId)) { - throw new IllegalArgumentException(); + throw new DuplicateLikeException(); } } } From 6163d8072e3e5d127fef0e95ebbd5d16263c05ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Wed, 26 Mar 2025 17:53:21 +0900 Subject: [PATCH 29/37] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20API=20=EC=9D=BC=EB=B6=80=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log4u/domain/comment/testController/TestController.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java index 932ff616..8967f3f3 100644 --- a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java +++ b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java @@ -31,10 +31,4 @@ public String testLog4uException() { throw new NotFoundCommentException(); // 또는 임의의 ServiceException } - @GetMapping("/unknown") - public String testUnexpectedException() { - String str = null; - str.length(); // NPE - return "절대 도달하지 않음"; - } } From fe0f0f8b6c726de273ea31bd94657c63060ebea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Thu, 27 Mar 2025 00:53:13 +0900 Subject: [PATCH 30/37] =?UTF-8?q?test:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=84=A4=EB=AA=85,=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=95=8C=EB=A7=9E=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/like/service/LikeServiceTest.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java b/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java index 1152cc17..a79f6235 100644 --- a/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java +++ b/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.BDDMockito.*; +import java.util.Optional; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -12,10 +14,13 @@ import org.mockito.junit.jupiter.MockitoExtension; import com.example.log4u.domain.diary.entity.Diary; +import com.example.log4u.domain.diary.exception.NotFoundDiaryException; import com.example.log4u.domain.diary.service.DiaryService; import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; +import com.example.log4u.domain.like.dto.response.LikeCancelResponseDto; import com.example.log4u.domain.like.entity.Like; +import com.example.log4u.domain.like.exception.DuplicateLikeException; import com.example.log4u.domain.like.repository.LikeRepository; import com.example.log4u.domain.user.entity.User; import com.example.log4u.fixture.DiaryFixture; @@ -36,22 +41,22 @@ public class LikeServiceTest { private DiaryService diaryService; @Test - @DisplayName("성공 테스트: 사용자가 게시물에 좋아요를 누르면 좋아요가 저장된다") + @DisplayName("성공 테스트: 좋아요 추가 ") void likeSuccess() { // given - User user = UserFixture.createUserFixture(); - Diary diary = DiaryFixture.createDiaryFixture(); - LikeAddRequestDto requestDto = new LikeAddRequestDto(diary.getDiaryId()); + Long userId = 1L; + Long diaryId = 123L; + LikeAddRequestDto requestDto = new LikeAddRequestDto(diaryId); - Like like = LikeFixture.createLikeFixture(123243L, user.getUserId(), diary.getDiaryId()); + Like like = LikeFixture.createLikeFixture(123243L, userId, diaryId); Long updatedLikeCount = 11L; - given(likeRepository.existsByUserIdAndDiaryId(user.getUserId(), diary.getDiaryId())).willReturn(false); + given(likeRepository.existsByUserIdAndDiaryId(userId, diaryId)).willReturn(false); given(likeRepository.save(any(Like.class))).willReturn(like); - given(diaryService.incrementLikeCount(diary.getDiaryId())).willReturn(updatedLikeCount); + given(diaryService.incrementLikeCount(diaryId)).willReturn(updatedLikeCount); // when - LikeAddResponseDto response = likeService.addLike(user.getUserId(), requestDto); + LikeAddResponseDto response = likeService.addLike(userId, requestDto); // then verify(likeRepository).save(any(Like.class)); @@ -60,7 +65,7 @@ void likeSuccess() { } @Test - @DisplayName("예외 테스트: 존재하지 않는 다이어리에 좋아요 요청 시 예외가 발생한다") + @DisplayName("예외 테스트: 좋아요 추가 - 존재하지 않는 다이어리에 좋아요 요청") void likeFail_whenDiaryNotFound() { // given Long userId = 1L; @@ -68,10 +73,10 @@ void likeFail_whenDiaryNotFound() { LikeAddRequestDto requestDto = new LikeAddRequestDto(diaryId); given(likeRepository.existsByUserIdAndDiaryId(userId, diaryId)).willReturn(false); - given(diaryService.incrementLikeCount(diaryId)).willThrow(new IllegalArgumentException()); + given(diaryService.incrementLikeCount(diaryId)).willThrow(new NotFoundDiaryException()); // when & then - assertThrows(IllegalArgumentException.class, () -> { + assertThrows(NotFoundDiaryException.class, () -> { likeService.addLike(userId, requestDto); }); @@ -79,7 +84,7 @@ void likeFail_whenDiaryNotFound() { } @Test - @DisplayName("예외 테스트: 사용자가 이미 좋아요를 누른 다이어리에 또 요청하면 예외가 발생한다") + @DisplayName("예외 테스트: 좋아요 추가 - 이미 누른 좋아요 또 요청") void likeFail_whenAlreadyLiked() { // given Long userId = 1L; @@ -89,7 +94,7 @@ void likeFail_whenAlreadyLiked() { given(likeRepository.existsByUserIdAndDiaryId(userId, diaryId)).willReturn(true); // when & then - assertThrows(IllegalArgumentException.class, () -> { + assertThrows(DuplicateLikeException.class, () -> { likeService.addLike(userId, requestDto); }); From a97d94f772a90956a75c3d9cf9e080d14fdff995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Thu, 27 Mar 2025 00:54:09 +0900 Subject: [PATCH 31/37] =?UTF-8?q?feat:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log4u/domain/diary/entity/Diary.java | 7 ++++++- .../domain/diary/service/DiaryService.java | 12 +++++++++++- .../domain/like/controller/LikeController.java | 11 +++++++++++ .../dto/response/LikeCancelResponseDto.java | 10 ++++++++++ .../domain/like/repository/LikeRepository.java | 5 +++++ .../log4u/domain/like/service/LikeService.java | 17 +++++++++++++++++ 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/example/log4u/domain/like/dto/response/LikeCancelResponseDto.java diff --git a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java index a3cbadcb..9f558d7f 100644 --- a/src/main/java/com/example/log4u/domain/diary/entity/Diary.java +++ b/src/main/java/com/example/log4u/domain/diary/entity/Diary.java @@ -46,8 +46,13 @@ public class Diary extends BaseEntity { @Column(nullable = false) private Long likeCount; - public Long updateLikeCount() { + public Long incrementLikeCount() { this.likeCount++; return this.likeCount; } + + public Long decreaseLikeCount() { + this.likeCount--; + return this.likeCount; + } } diff --git a/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java b/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java index f3755e51..48b95d34 100644 --- a/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java +++ b/src/main/java/com/example/log4u/domain/diary/service/DiaryService.java @@ -22,6 +22,16 @@ public Diary getDiary(Long diaryId) { public Long incrementLikeCount(Long diaryId) { Diary diary = getDiary(diaryId); - return diary.updateLikeCount(); + return diary.incrementLikeCount(); + } + + public Long decreaseLikeCount(Long diaryId) { + Diary diary = getDiary(diaryId); + return diary.decreaseLikeCount(); + } + + public Long getLikeCount(Long diaryId) { + Diary diary = getDiary(diaryId); + return diary.getLikeCount(); } } diff --git a/src/main/java/com/example/log4u/domain/like/controller/LikeController.java b/src/main/java/com/example/log4u/domain/like/controller/LikeController.java index 7b5d7e3d..e0922c73 100644 --- a/src/main/java/com/example/log4u/domain/like/controller/LikeController.java +++ b/src/main/java/com/example/log4u/domain/like/controller/LikeController.java @@ -1,6 +1,8 @@ package com.example.log4u.domain.like.controller; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -8,6 +10,7 @@ import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; +import com.example.log4u.domain.like.dto.response.LikeCancelResponseDto; import com.example.log4u.domain.like.service.LikeService; import io.swagger.v3.oas.annotations.tags.Tag; @@ -29,4 +32,12 @@ public ResponseEntity addLike(@Valid @RequestBody LikeAddReq LikeAddResponseDto response = likeService.addLike(userId, requestDto); return ResponseEntity.ok(response); } + + @DeleteMapping("/{diaryId}") + public ResponseEntity cancelLike(@PathVariable Long diaryId){ + Long userId = 1L;// 실제 구현에서는 토큰에서 추출 + + LikeCancelResponseDto response = likeService.cancelLike(userId, diaryId); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/com/example/log4u/domain/like/dto/response/LikeCancelResponseDto.java b/src/main/java/com/example/log4u/domain/like/dto/response/LikeCancelResponseDto.java new file mode 100644 index 00000000..5c778678 --- /dev/null +++ b/src/main/java/com/example/log4u/domain/like/dto/response/LikeCancelResponseDto.java @@ -0,0 +1,10 @@ +package com.example.log4u.domain.like.dto.response; + +public record LikeCancelResponseDto( + boolean liked, + Long likeCount) { + + public static LikeCancelResponseDto of(boolean liked, Long likeCount) { + return new LikeCancelResponseDto(liked, likeCount); + } +} diff --git a/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java b/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java index 8b521de8..01997377 100644 --- a/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java +++ b/src/main/java/com/example/log4u/domain/like/repository/LikeRepository.java @@ -1,9 +1,14 @@ package com.example.log4u.domain.like.repository; +import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import com.example.log4u.domain.like.entity.Like; public interface LikeRepository extends JpaRepository { boolean existsByUserIdAndDiaryId(Long userId, Long diaryId); + + Optional findByUserIdAndDiaryId(Long userId, Long diaryId); } diff --git a/src/main/java/com/example/log4u/domain/like/service/LikeService.java b/src/main/java/com/example/log4u/domain/like/service/LikeService.java index a48d7d2f..4f368cb7 100644 --- a/src/main/java/com/example/log4u/domain/like/service/LikeService.java +++ b/src/main/java/com/example/log4u/domain/like/service/LikeService.java @@ -1,11 +1,14 @@ package com.example.log4u.domain.like.service; +import java.util.Optional; + import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.example.log4u.domain.diary.service.DiaryService; import com.example.log4u.domain.like.dto.request.LikeAddRequestDto; import com.example.log4u.domain.like.dto.response.LikeAddResponseDto; +import com.example.log4u.domain.like.dto.response.LikeCancelResponseDto; import com.example.log4u.domain.like.entity.Like; import com.example.log4u.domain.like.exception.DuplicateLikeException; import com.example.log4u.domain.like.repository.LikeRepository; @@ -30,6 +33,20 @@ public LikeAddResponseDto addLike(Long userId, LikeAddRequestDto requestDto) { return LikeAddResponseDto.of(true, likeCount); } + @Transactional + public LikeCancelResponseDto cancelLike(Long userId, Long diaryId) { + return likeRepository.findByUserIdAndDiaryId(userId, diaryId) + .map(like -> { + likeRepository.delete(like); + Long likeCount = diaryService.decreaseLikeCount(diaryId); + return LikeCancelResponseDto.of(false, likeCount); + }) + .orElseGet(() -> { + Long currentCount = diaryService.getLikeCount(diaryId); + return LikeCancelResponseDto.of(false, currentCount); + }); + } + private void validateDuplicateLike(Long userId, Long diaryId) { if (likeRepository.existsByUserIdAndDiaryId(userId, diaryId)) { throw new DuplicateLikeException(); From e237e94cc783c75621024dd851f84ba39238c9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Thu, 27 Mar 2025 00:54:34 +0900 Subject: [PATCH 32/37] =?UTF-8?q?test:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20API=20=EB=8B=A8=EC=9C=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/like/service/LikeServiceTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java b/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java index a79f6235..36c14b2c 100644 --- a/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java +++ b/src/test/java/com/example/log4u/domain/like/service/LikeServiceTest.java @@ -100,4 +100,50 @@ void likeFail_whenAlreadyLiked() { verify(likeRepository, never()).save(any(Like.class)); } + + @DisplayName("성공 테스트: 좋아요 취소") + @Test + void cancelLike_Success() { + Long userId = 1L; + Long diaryId = 123L; + + Like like = LikeFixture.createLikeFixture(123243L, userId, diaryId); + Long updatedLikeCount = 11L; + + given(likeRepository.findByUserIdAndDiaryId(userId, diaryId)).willReturn(Optional.of(like)); + doNothing().when(likeRepository).delete(like); + given(diaryService.decreaseLikeCount(diaryId)).willReturn(updatedLikeCount); + + // when + LikeCancelResponseDto response = likeService.cancelLike(userId, diaryId); + + // then + verify(likeRepository).delete(like); + verify(diaryService).decreaseLikeCount(diaryId); + + assertThat(response.liked()).isFalse(); + assertThat(response.likeCount()).isEqualTo(updatedLikeCount); + } + + @DisplayName("성공 테스트: 좋아요 취소 - 존재하지 않는 좋아요에 대해 동일한 결과 반환)") + @Test + void cancelLike_NoLikeExists() { + // given + Long userId = 1L; + Long diaryId = 100L; + Long currentCount = 5L; + + given(likeRepository.findByUserIdAndDiaryId(userId, diaryId)).willReturn(Optional.empty()); + given(diaryService.getLikeCount(diaryId)).willReturn(currentCount); + + // when + LikeCancelResponseDto response = likeService.cancelLike(userId, diaryId); + + // then + verify(likeRepository, never()).delete(any()); + verify(diaryService).getLikeCount(diaryId); + + assertThat(response.liked()).isFalse(); + assertThat(response.likeCount()).isEqualTo(currentCount); + } } From ecf432bfa2a7d9770a888d0f672adb7b94098ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Thu, 27 Mar 2025 00:58:09 +0900 Subject: [PATCH 33/37] =?UTF-8?q?remove:=20Test=20API=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/comment/testController/TestController.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java index 932ff616..268a29cd 100644 --- a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java +++ b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java @@ -30,11 +30,5 @@ public String testIllegalArgument() { public String testLog4uException() { throw new NotFoundCommentException(); // 또는 임의의 ServiceException } - - @GetMapping("/unknown") - public String testUnexpectedException() { - String str = null; - str.length(); // NPE - return "절대 도달하지 않음"; - } + } From e8620b279abb94b22fceac2b1bf3aa218926d481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B1=B4=ED=9D=AC?= Date: Thu, 27 Mar 2025 09:47:13 +0900 Subject: [PATCH 34/37] test: PR GPT TST --- .../log4u/domain/comment/testController/TestController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java index 8967f3f3..dfb57508 100644 --- a/src/main/java/com/example/log4u/domain/comment/testController/TestController.java +++ b/src/main/java/com/example/log4u/domain/comment/testController/TestController.java @@ -31,4 +31,5 @@ public String testLog4uException() { throw new NotFoundCommentException(); // 또는 임의의 ServiceException } + //test } From 612e2f5736601da1552ed14f9d65c03e4d829175 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Thu, 27 Mar 2025 10:11:49 +0900 Subject: [PATCH 35/37] =?UTF-8?q?chore:=20=EB=B8=8C=EB=9E=9C=EC=B9=98?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20dev=20->=20develop=20chore:=20?= =?UTF-8?q?=EC=86=8C=EB=82=98=ED=81=B4=EB=9D=BC=EC=9A=B0=EB=93=9C=20?= =?UTF-8?q?=EC=8B=9C=ED=81=AC=EB=A6=BF=20=ED=82=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/daily-archive.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily-archive.yml b/.github/workflows/daily-archive.yml index a201e782..aba01d3a 100644 --- a/.github/workflows/daily-archive.yml +++ b/.github/workflows/daily-archive.yml @@ -22,7 +22,7 @@ jobs: - name: Create new archive branch run: | - git checkout dev - git pull origin dev + git checkout develop + git pull origin develop git checkout -b ${{ env.BRANCH_NAME }} git push origin ${{ env.BRANCH_NAME }} \ No newline at end of file From 508113614021e42da4ef7c9a700279107aced558 Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Thu, 27 Mar 2025 10:14:56 +0900 Subject: [PATCH 36/37] =?UTF-8?q?chore:=20develop=20=EC=9C=BC=EB=A1=9C=20p?= =?UTF-8?q?r=20=EC=8B=9C=20=EC=9E=90=EC=BD=94=EC=BD=94=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca8c9427..772a9499 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,10 +2,11 @@ name: log4u-build on: # push: # branches: -# - dev # dev 브랜치 push +# - develop # dev 브랜치 push pull_request: branches: - main # main pr + - develop # develop pr types: [ opened, synchronize, reopened ] jobs: build: From 9133254834e337caf0a2d7a96a99b5cf5ed8500d Mon Sep 17 00:00:00 2001 From: ByeongGyu Jeon Date: Thu, 27 Mar 2025 10:24:47 +0900 Subject: [PATCH 37/37] =?UTF-8?q?chore:=20sonarcloud=20branch=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 2 +- build.gradle.kts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 772a9499..53ba9c8d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,4 +42,4 @@ jobs: DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Database password run: | chmod +x ./gradlew - ./gradlew build jacocoTestReport sonar --info \ No newline at end of file + ./gradlew build jacocoTestReport sonar --info -Dsonar.branch.name=${{ github.ref_name }} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 0ad7d420..cda4496d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -74,5 +74,6 @@ sonar { property("sonar.host.url", "https://sonarcloud.io") property("sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacoco/test/jacocoTestReport.xml") property("sonar.java.checkstyle.reportPaths", "build/reports/checkstyle/main.xml") + property("sonar.branch.name", System.getenv("BRANCH_NAME") ?: "main") } } \ No newline at end of file