Skip to content

Commit

Permalink
Merge pull request #276 from woowacourse-teams/dev/be
Browse files Browse the repository at this point in the history
로그 및 액션 파일 최신화를 위한 PR
  • Loading branch information
jminkkk authored Aug 6, 2024
2 parents 18f70f1 + bd19df5 commit d774a7f
Show file tree
Hide file tree
Showing 24 changed files with 397 additions and 97 deletions.
84 changes: 41 additions & 43 deletions .github/workflows/backend_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,55 @@ on:
- main

jobs:
find-branch:
runs-on: self-hosted
steps:
- name: 브랜치명을 통해 개발 환경 알아내기
run: |
cd ${{ secrets.SCRIPT_DIRECTORY }}
bash find-env-by-branch.sh
build:
runs-on: ubuntu-latest
env:
build-directory: ./backend
needs: find-branch
runs-on: self-hosted
steps:
- name: Checkout
- name: 체크아웃
uses: actions/checkout@v4

- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin

- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: gradle 캐싱
uses: gradle/actions/setup-gradle@v3

- name: Build BootJar
run: ./gradlew bootJar
working-directory: ${{ env.build-directory }}

- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: code-zap-jar
path: backend/build/libs/*.jar

- name: bootJar로 jar 파일 생성
run: |
./gradlew bootJar
mv build/libs/*.jar ${{ secrets.WORK_DIRECTORY }}/${{ env.ENVIRONMENT }}
working-directory: ./backend

- name: 클린업
if: always()
run: rm -rf ../2024-code-zap/*
deploy:
needs: build
runs-on: self-hosted
steps:
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: code-zap-jar
path: ${{ secrets.WORK_DIRECTORY }}

- name: Run Deploy Script
- name: 브랜치명을 통해 개발 환경 알아내기
run: |
cd ${{ secrets.WORK_DIRECTORY }}
unzip -o code-zap-jar.zip
RUNNER_TRACKING_ID="" && ./deploy.sh
cd ${{ secrets.SCRIPT_DIRECTORY }}
bash find-env-by-branch.sh
- name: Verify Deploy Succeed
- name: 실행 프로세스 확인
run: |
sleep 3
pgrep -f 'java -jar .*code-zap.*\.jar' || { echo "Deploy Failed"; exit 1; }
cd ${{ secrets.SCRIPT_DIRECTORY }}
bash check-old-pids.sh
- name: 배포 스크립트 실행
run: |
cd ${{ secrets.SCRIPT_DIRECTORY }}
RUNNER_TRACKING_ID="" && bash start.sh ${{ env.ENVIRONMENT }}
- name: 실행 프로세스 확인으로 배포 검증
run: |
cd ${{ secrets.SCRIPT_DIRECTORY }}
bash verify-deploy.sh
43 changes: 23 additions & 20 deletions .github/workflows/backend_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@ name: Backend CI
on:
pull_request:
branches:
- '**'
- dev/be
- develop
- main

jobs:
build:
runs-on: ubuntu-latest
env:
build-directory: ./backend
runs-on: self-hosted

steps:
- name: Checkout
- name: 체크아웃
uses: actions/checkout@v4
- name: Start MySQL
uses: mirromutth/[email protected]
with:
host port: 23306
container port: 3306
mysql version: lts
mysql database: code_zap
mysql root password: woowacourse
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
- name: Test with Gradle Wrapper

- name: H2 스프링 설정 생성
run: |
echo "spring:
h2:
console:
enabled: true
path: /h2-console
datasource:
url: jdbc:h2:mem:database" > backend/src/main/resources/application.yml
- name: 테스트 코드 실행
run: ./gradlew test
working-directory: ${{ env.build-directory }}
working-directory: ./backend

- name: 클린업
if: always()
run: rm -rf ../2024-code-zap/*
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,3 @@ JWT와 리프레시 토큰으로 로그인 기능을 구현해야 하는 순간,
</table>

<br>

3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ out/

### macOS ###
.DS_Store

### YAML ###
application-db.yml
5 changes: 3 additions & 2 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ dependencies {

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'

compileOnly 'org.projectlombok:lombok:0.11.0'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

runtimeOnly 'com.mysql:mysql-connector-j:9.0.0'

runtimeOnly 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:5.5.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Expand Down
9 changes: 0 additions & 9 deletions backend/docker/docker-compose.yml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler
public ResponseEntity<ProblemDetail> handleCodeZapException(CodeZapException codeZapException) {
log.info("[CodeZapException] {}가 발생했습니다.", codeZapException.getClass().getName(), codeZapException);
return ResponseEntity.status(codeZapException.getHttpStatusCode())
.body(ProblemDetail.forStatusAndDetail(
codeZapException.getHttpStatusCode(),
Expand All @@ -32,6 +36,7 @@ public ResponseEntity<ProblemDetail> handleMethodArgumentNotValidException(
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.toList();

log.info("[MethodArgumentNotValidException] {}가 발생했습니다. \n", exception.getClass().getName(), exception);
return ResponseEntity.badRequest()
.body(ProblemDetail.forStatusAndDetail(
HttpStatus.BAD_REQUEST,
Expand All @@ -41,6 +46,7 @@ public ResponseEntity<ProblemDetail> handleMethodArgumentNotValidException(

@ExceptionHandler
public ResponseEntity<ProblemDetail> handleException(Exception exception) {
log.error("[Exception] 예상치 못한 오류 {} 가 발생했습니다.", exception.getClass().getName(), exception);
return ResponseEntity.internalServerError()
.body(ProblemDetail.forStatusAndDetail(
HttpStatus.INTERNAL_SERVER_ERROR,
Expand Down
35 changes: 35 additions & 0 deletions backend/src/main/java/codezap/global/logger/MDCFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package codezap.global.logger;

import java.io.IOException;
import java.util.UUID;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class MDCFilter implements Filter {
private final String CORRELATION_ID = "correlationId";

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
MDC.put(CORRELATION_ID, generateCorrelationId());
chain.doFilter(request, response);
MDC.clear();
}

private String generateCorrelationId() {
return UUID.randomUUID()
.toString()
.substring(0, 8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package codezap.global.logger;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class MethodExecutionTimeAspect {

@Around("execution(* codezap..*(..)) && " +
"!execution(* codezap.global.logger.MethodExecutionTimeAspect(..))" +
"!execution(* codezap.global.exception.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
if (!log.isInfoEnabled()) {
return joinPoint.proceed();
}

long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();

long executionTimeMillis = endTime - startTime;

String className = joinPoint.getSignature()
.getDeclaringType()
.getSimpleName();
String methodName = joinPoint.getSignature()
.getName();

log.info("{}.{} 실행 {}ms", className, methodName, executionTimeMillis);

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package codezap.global.logger;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class RequestResponseLogger extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

long startTime = System.currentTimeMillis();
filterChain.doFilter(requestWrapper, responseWrapper);
long duration = System.currentTimeMillis() - startTime;

String requestBody = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
String responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);

log.info("[Request] {}, {}, 요청 바디: {}", request.getMethod(), request.getRequestURI(), requestBody);
log.info("[Response] Status: {}, Duration: {}ms, 응답 바디: {}", response.getStatus(), duration, responseBody);

responseWrapper.copyBodyToResponse();
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return path.contains("/swagger") || path.contains("/v3/api-docs");
}
}
29 changes: 29 additions & 0 deletions backend/src/main/java/codezap/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package codezap.member.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class Member {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true, nullable = false)
private String email;

@Column(nullable = false)
private String password;

@Column(unique = true, nullable = false)
private String username;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package codezap.member.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import codezap.member.domain.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ public interface SpringDocTemplateController {
* 모든 스니펫 순서는 1부터 시작합니다. \n
* 스니펫 순서는 오름차순으로 정렬하여 보내야 합니다. \n
""")
@ApiResponse(responseCode = "201", description = "회원 예약 생성 성공", headers = {
@ApiResponse(responseCode = "201", description = "템플릿 생성 성공", headers = {
@Header(name = "생성된 템플릿의 API 경로", example = "/templates/1")})
@ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1", errorCases = {
@ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates", errorCases = {
@ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "템플릿 이름 null 입니다."),
@ErrorCase(description = "제목 또는 스니펫 파일명이 255자를 초과한 경우", exampleMessage = "제목은 최대 255자까지 입력 가능합니다."),
@ErrorCase(description = "썸네일 스니펫의 순서가 1이 아닌 경우", exampleMessage = "썸네일 스니펫의 순서가 잘못되었습니다."),
Expand Down
Loading

0 comments on commit d774a7f

Please sign in to comment.