CI/CD
CI/CD(Continuous Integration/Continuous Delivery 또는 Continuous Deployment)는 소프트웨어 개발 프로세스를 자동화하여 코드 변경 사항을 효율적으로 빌드, 테스트, 배포하는 방법이다.
CI(Continuous Integration)
- 코드 통합: 작업을 마무리하고 여러 사람의 코드들을 공용 코드 베이스로 최신 상태로 유지한다.
- 자동화된 빌드: 통합된 코드를 자동으로 빌드한다. 이 과정에서 컴파일을 통해 정상적으로 실행 되는지 확인 한다.
- 자동화된 테스트: 빌드가 완료 된 후, 자동화된 테스트를 실행하여 빌드된 코드가 예상대로 동작하는지 새로운 코드 변경이 기존 기능에 문제를 일으키지 않는지 검증한다.
원래는 이순서이지만 나는 테스트랑 빌드의 순서를 바꿔서 진행한다.
왜냐하면 테스트 후에 빌드시 jar 파일이 정상 동작하는 코드로 이루어 졌다는것을 보장 할 수 있다 즉 문제가 해결되지 않은 코드를 빌드하고 배포하는것을 막을 수 있다.
1. 통합 테스트 및 API문서
설정
맨처음 빌드 그레이들로 가서 레스트 덕 추가하기

API문서 만들 때의 레스트 덕이랑 스웨거 차이
레스트덕:프로덕션 코드에 영향이 없다 테스트 코드만 돼있으면 된다.
통합 테스트 케이스를 기반으로 문서를 생성하기 때문이다.
스웨거:프로덕션 코드가 영향이있는데 프로덕션 코드란 실제 코드가 완성이 돼야된다? 대신 테스트 코드가 필요 없음 스웨거가 API문서 자체는 이쁘게 나오는데 밑 코드가 컨트롤러에 붙어야 한다.
개발자 입장에서는 코드 더러움



build gradle에 라이브러리 추가하기

plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
group = 'shop.mtcoding'
version = '1.0'
java {
sourceCompatibility = '21'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// 서드파티
implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation group: 'org.qlrm', name: 'qlrm', version: '4.0.1'
implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4'
// 기본
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
tasks.named('test') {
useJUnitPlatform()
}
jar {
enabled = false
}
ext {
set('snippetsDir', file("build/generated-snippets"))
}
// src/docs 이하면 *.adoc 파일을 테스트시에 찾아내서 html 파일을 생성해줌
tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
// jar에 api.html에 옮기는 코드
bootJar {
dependsOn asciidoctor
copy {
from "${asciidoctor.outputDir}"
into 'src/main/resources/static/docs' // /static/docs로 복사!
}
}
레스트 덕 추가하기

package shop.mtcoding.blog.controller;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.operation.preprocess.Preprocessors;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import java.nio.charset.StandardCharsets;
@ExtendWith({ SpringExtension.class, RestDocumentationExtension.class })
public class MyRestDoc {
protected MockMvc mvc;
protected RestDocumentationResultHandler document;
@BeforeEach
public void setup(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.document = MockMvcRestDocumentation.document("{class-name}/{method-name}",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()));
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(new CharacterEncodingFilter(StandardCharsets.UTF_8.name(),
true))
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocumentation))
// .apply(SecurityMockMvcConfigurers.springSecurity())
.alwaysDo(document)
.build();
}
}
각 테스트 코드 밑에 붙여주기
이때 모든 컨트롤러 myRestduck 상속받기

통합 테스트 실습
package com.example.finalproject.domain.user;
import com.example.finalproject._core.utils.AppJwtUtil;
import com.example.finalproject.domain.photo.Photo;
import com.example.finalproject.domain.user.User;
import com.example.finalproject.domain.user.UserRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.hamcrest.Matchers.containsString;
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class UserControllerTest {
private ObjectMapper om = new ObjectMapper();
@Autowired
private MockMvc mvc;
private static String jwt;
@BeforeAll
public static void setUp() {
jwt = AppJwtUtil.create(
User.builder()
.id(3)
.myName("변우석")
.email("bunwuseok@example.com")
.blueChecked(true)
.build());
}
//회원가입
@Test
public void join_test() throws Exception {
// given
UserRequest.JoinDTO reqDTO = new UserRequest.JoinDTO();
reqDTO.setEmail("p4rk@naver.com");
reqDTO.setMyName("p4rk");
reqDTO.setNickName("cat");
reqDTO.setPassword("1234");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
post("/user/join")
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// System.out.println("respBody: " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.email").value("p4rk@naver.com"));
actions.andExpect(jsonPath("$.response.nickName").value("cat"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
//아이디 중복체크
@Test
public void join_username_same_fail_test() throws Exception {
// given
UserRequest.JoinDTO reqDTO = new UserRequest.JoinDTO();
reqDTO.setEmail("junghein@example.com");
reqDTO.setMyName("dfd");
reqDTO.setNickName("dfdfd");
reqDTO.setPassword("1234");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
post("/user/join")
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody: " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(400));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("중복된 이메일이 있습니다."));
}
//회원가입 유효성검사
@Test
public void join_username_valid_fail_test() throws Exception {
// given
UserRequest.JoinDTO reqDTO = new UserRequest.JoinDTO();
reqDTO.setEmail("a");
reqDTO.setMyName("dfd");
reqDTO.setNickName("cat");
reqDTO.setPassword("1234");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
post("/user/join")
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody: " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(400));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("이메일은 최소 3자 이상 최대 20자 이하여야 합니다. : email"));
}
//로그인
@Test
public void login_success_test() throws Exception {
// given
UserRequest.LoginDTO reqDTO = new UserRequest.LoginDTO();
reqDTO.setEmail("junghein@example.com");
reqDTO.setPassword("1234");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
post("/user/login")
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
//eye
String respBody = actions.andReturn().getResponse().getContentAsString();
String jwt = actions.andReturn().getResponse().getHeader("Authorization");
// then
actions.andExpect(status().isOk()); // header 검증
actions.andExpect(result -> result.getResponse().getHeader("Authorization").contains("Bearer " + jwt));
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.email").value("junghein@example.com"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
//로그인 실패 테스트
@Test
public void login_fail_test() throws Exception {
// given
UserRequest.LoginDTO reqDTO = new UserRequest.LoginDTO();
reqDTO.setEmail("junghein@example.com");
reqDTO.setPassword("12345");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
post("/user/login")
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// then
actions.andExpect(status().isUnauthorized()); // header 검증
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.errorMessage").value("사용자 정보를 찾을 수 없습니다."));
actions.andExpect(jsonPath("$.response").isEmpty());
}
//로그인 유효성검사
@Test
public void login_username_valid_fail_test() throws Exception {
// given
UserRequest.LoginDTO reqDTO = new UserRequest.LoginDTO();
reqDTO.setEmail("");
reqDTO.setPassword("1234");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
post("/user/login")
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(jsonPath("$.status").value(400));
actions.andExpect(jsonPath("$.errorMessage").value("이메일이 공백일 수 없습니다 : email"));
}
//앱 세팅 테스트 (사용자 변경)
@Test
public void setting_success_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(2)
.blueChecked(false)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/setting")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// System.out.println("respBody:"+respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.id").value(2));
actions.andExpect(jsonPath("$.response.myName").value("임시완"));
actions.andExpect(jsonPath("$.response.email").value("limsiwan@example.com"));
actions.andExpect(jsonPath("$.response.nickName").value("limsiwan"));
actions.andExpect(jsonPath("$.response.mobile").value("010-9876-5432"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void setting_fail_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(999)
.blueChecked(false)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/setting")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// System.out.println("respBody: " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("인증되지 않았습니다."));
}
//앱 프로필 테스트
@Test
public void profile_success_test() throws Exception {
// given
String jwt = AppJwtUtil.create(User.builder()
.id(2)
.blueChecked(false)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/profile")
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.userId").value(2));
actions.andExpect(jsonPath("$.response.myName").value("임시완"));
actions.andExpect(jsonPath("$.response.email").value("limsiwan@example.com"));
actions.andExpect(jsonPath("$.response.nickName").value("limsiwan"));
actions.andExpect(jsonPath("$.response.mobile").value("010-9876-5432"));
actions.andExpect(jsonPath("$.response.photoDTO.id").value(5));
actions.andExpect(jsonPath("$.response.photoDTO.name").exists());
actions.andExpect(jsonPath("$.response.photoDTO.photoPath").value("/upload/user/user2.webp"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void profile_fail_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(999)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/profile")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("인증되지 않았습니다."));
}
// 크리에이터 지원 페이지
@Test
public void creator_apply_form_success_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(1)
.blueChecked(false)
.photo(Photo.builder()
.uuidName("uuid_사용자사진1")
.path("/upload/user/user1.webp")
.build())
.nickName("junghein")
.introMsg("연예인 체형")
.build());
// when
ResultActions actions = mvc.perform(
get("/app/creator-apply-form")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println(respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.id").value(1));
actions.andExpect(jsonPath("$.response.name").value("정해인"));
actions.andExpect(jsonPath("$.response.instagram").value("holyhaein"));
actions.andExpect(jsonPath("$.response.blueChecked").value("false"));
actions.andExpect(jsonPath("$.response.status").value("신청 전"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void creator_apply_form_fail_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(999)
.blueChecked(false)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/creator-apply-form")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("인증되지 않았습니다."));
}
//사용자 크리에이터 지원하기
@Test
public void creator_apply_success_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(1)
.blueChecked(false)
.photo(Photo.builder()
.uuidName("uuid_사용자사진1")
.path("/upload/user/user1.webp")
.build())
.nickName("junghein")
.introMsg("연예인 체형")
.build());
UserRequest.CreatorApplyDTO reqDTO = new UserRequest.CreatorApplyDTO();
reqDTO.setHeight("182");
reqDTO.setComment("키 182 연예인 체형입니다.");
reqDTO.setInstagram("abc@naver.com");
reqDTO.setJob("학생");
reqDTO.setWeight("72");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
put("/app/creator-apply")
.header("Authorization", "Bearer " + jwt1)
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
//eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// System.out.println(respBody);
String respJwt = actions.andReturn().getResponse().getHeader("Authorization");
// then
actions.andExpect(status().isOk()); // 상태 코드 검증
if (respJwt != null) {
assertTrue(respJwt.contains("Bearer "));
}
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.id").value(1));
actions.andExpect(jsonPath("$.response.name").value("정해인"));
actions.andExpect(jsonPath("$.response.instagram").value("abc@naver.com"));
actions.andExpect(jsonPath("$.response.blueChecked").value(false));
actions.andExpect(jsonPath("$.response.status").value("승인 대기"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void creator_apply_fail_test() throws Exception {
// given
UserRequest.CreatorApplyDTO reqDTO = new UserRequest.CreatorApplyDTO();
reqDTO.setHeight("");
reqDTO.setComment("키 182 연예인 체형입니다.");
reqDTO.setInstagram("abc@naver.com");
reqDTO.setJob("학생");
reqDTO.setWeight("72");
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
put("/app/creator-apply")
.header("Authorization", "Bearer " + jwt)
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
//eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// System.out.println(respBody);
String respJwt = actions.andReturn().getResponse().getHeader("Authorization");
// then
if (respJwt != null) {
assertTrue(respJwt.contains("Bearer "));
}
actions.andExpect(jsonPath("$.status").value(400));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("키는 공백일 수 없습니다 : height"));
}
//크리에이터 뷰 페이지
@Test
public void creator_view_success_test() throws Exception {
// given
Integer userId = 3;
// when
ResultActions actions = mvc.perform(
get("/app/creator-view/" + userId)
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println(respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.userDTO.creatorId").value(3));
actions.andExpect(jsonPath("$.response.userDTO.blueChecked").value(true));
actions.andExpect(jsonPath("$.response.userDTO.photoName").exists());
actions.andExpect(jsonPath("$.response.userDTO.photoPath").exists());
actions.andExpect(jsonPath("$.response.userDTO.nickName").value("bun"));
actions.andExpect(jsonPath("$.response.userDTO.height").value("180cm"));
actions.andExpect(jsonPath("$.response.userDTO.weight").value("75kg"));
actions.andExpect(jsonPath("$.response.userDTO.job").value("직장인"));
actions.andExpect(jsonPath("$.response.userDTO.introMsg").value("연예인 체형"));
actions.andExpect(jsonPath("$.response.codiList[0].codiId").value(1));// Json데이터가 배열이면 몇번지의 데이터인지 기입해줘야 함.
actions.andExpect(jsonPath("$.response.codiList[0].codiPhotoId").value(14));
actions.andExpect(jsonPath("$.response.codiList[0].photoName").exists());
actions.andExpect(jsonPath("$.response.codiList[0].photoPath").exists());
actions.andExpect(jsonPath("$.response.codiList[0].codiPhoto").value("CODI"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void creator_view_fail_test() throws Exception {
// given
Integer userId = 999;
// when
ResultActions actions = mvc.perform(
get("/app/creator-view/" + userId)
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody: " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("크리에이터가 아닙니다."));
}
//유저 마이페이지
@Test
public void user_my_page_success_test() throws Exception {
// given
// when
ResultActions actions = mvc.perform(
get("/app/user-my-page")
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println(respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.userId").value(3));
actions.andExpect(jsonPath("$.response.photoName").exists());
actions.andExpect(jsonPath("$.response.photoPath").exists());
actions.andExpect(jsonPath("$.response.nickName").value("bun"));
actions.andExpect(jsonPath("$.response.orderCount").value(4));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void user_my_page_fail_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(999)
.blueChecked(false)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/user-my-page")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("인증되지 않았습니다."));
}
@Test
public void search_all_multiple_results_success_test() throws Exception {
// given
// when
ResultActions actions = mvc.perform(
get("/app/search-all")
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(status().isOk());
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
//첫번째 코디
actions.andExpect(jsonPath("$.response.codiListDTOS").isArray());
actions.andExpect(jsonPath("$.response.codiListDTOS[0].codiId").value(1));
actions.andExpect(jsonPath("$.response.codiListDTOS[0].codiPhotoId").value(14));
actions.andExpect(jsonPath("$.response.codiListDTOS[0].photoName").value("uuid_코디사진1"));
actions.andExpect(jsonPath("$.response.codiListDTOS[0].photoPath").value("/upload/codi/user-3-codi1.webp"));
// 첫번째 아이템
actions.andExpect(jsonPath("$.response.codiListDTOS").isArray());
actions.andExpect(jsonPath("$.response.itemListDTOS[0].itemId").value(1));
actions.andExpect(jsonPath("$.response.itemListDTOS[0].name").value("SCRAPPED 티셔츠(WHITE)"));
actions.andExpect(jsonPath("$.response.itemListDTOS[0].description").value("힙하고 유니크한 반팔로 어느 코디에도 잘 어울립니다."));
actions.andExpect(jsonPath("$.response.itemListDTOS[0].price").value(45000));
actions.andExpect(jsonPath("$.response.itemListDTOS[0].itemPhotoId").value(30));
actions.andExpect(jsonPath("$.response.itemListDTOS[0].itemPhotoName").value("uuid_아이템사진1"));
actions.andExpect(jsonPath("$.response.itemListDTOS[0].photoPath").value("/upload/items/item01/mainItemPhoto.jpg"));
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void creator_my_page_success_test() throws Exception {
// given
// when
ResultActions actions = mvc.perform(
get("/app/creator-my-page")
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody = " + respBody);
// then
actions.andExpect(status().isOk())
.andExpect(content().json(respBody))
.andExpect(jsonPath("$.status").value(200))
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.response.userDTO.creatorId").value(3))
.andExpect(jsonPath("$.response.userDTO.blueChecked").value(true))
.andExpect(jsonPath("$.response.userDTO.photoName").exists())
.andExpect(jsonPath("$.response.userDTO.photoPath").exists())
.andExpect(jsonPath("$.response.userDTO.nickName").value("bun"))
.andExpect(jsonPath("$.response.userDTO.height").value("180cm"))
.andExpect(jsonPath("$.response.userDTO.weight").value("75kg"))
.andExpect(jsonPath("$.response.userDTO.job").value("직장인"))
.andExpect(jsonPath("$.response.userDTO.introMsg").value("연예인 체형"))
.andExpect(jsonPath("$.response.userDTO.orderCount").value(4))
.andExpect(jsonPath("$.response.userDTO.mileage").value(3000))
.andExpect(jsonPath("$.response.codiList[0].codiId").value(1))
.andExpect(jsonPath("$.response.codiList[0].codiPhotoId").value(14))
.andExpect(jsonPath("$.response.codiList[0].photoName").value("uuid_코디사진1"))
.andExpect(jsonPath("$.response.codiList[0].photoPath").value("/upload/codi/user-3-codi1.webp"))
.andExpect(jsonPath("$.response.codiList[0].codiPhoto").value("CODI"))
.andExpect(jsonPath("$.response.itemList[0].itemId").value(5))
.andExpect(jsonPath("$.response.itemList[0].name").value("crop cable sweater"))
.andExpect(jsonPath("$.response.itemList[0].description").value("방모 원사임에도 모달이 섞여 기분좋은 찰랑거림이 있는게 매력적입니다."))
.andExpect(jsonPath("$.response.itemList[0].price").value(75000))
.andExpect(jsonPath("$.response.itemList[0].itemPhotoId").value(38))
.andExpect(jsonPath("$.response.itemList[0].itemPhotoName").value("uuid_아이템사진5"))
.andExpect(jsonPath("$.response.itemList[0].photoPath").value("/upload/items/item05/mainItemPhoto.jpg"))
.andExpect(jsonPath("$.response.itemList[0].itemPhoto").value("ITEM"))
.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void creator_my_page_fail_test() throws Exception {
// given
String jwt1 = AppJwtUtil.create(User.builder()
.id(1)
.blueChecked(false)
.photo(Photo.builder()
.uuidName("uuid_사용자사진1")
.path("/upload/user/user1.webp")
.build())
.nickName("junghein")
.height("175cm")
.weight("70kg")
.job("직장인")
.introMsg("연예인 체형")
.mileage(0)
.build());
// when
ResultActions actions = mvc.perform(
get("/app/creator-my-page")
.header("Authorization", "Bearer " + jwt1)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// then
actions.andExpect(jsonPath("$.status").value(401));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("인증 되지 않았습니다."));
}
@Test
public void user_profile_update_success_test() throws Exception {
// given
Integer userId = 3;
UserRequest.ProfileUpdateDTO reqDTO = new UserRequest.ProfileUpdateDTO();
reqDTO.setMyName("변우식");
reqDTO.setNickName("bun");
reqDTO.setPassword("12345");
UserRequest.ProfileUpdateDTO.PhotoDTO photoDTO = new UserRequest.ProfileUpdateDTO.PhotoDTO();
photoDTO.setName("uuid_사용자사진3");
photoDTO.setBase64("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9");
reqDTO.setPhoto(photoDTO);
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
put("/user/profile/" + userId)
.header("Authorization", "Bearer " + jwt)
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
//eye
String respBody = actions.andReturn().getResponse().getContentAsString();
// System.out.println(respBody);
String respJwt = actions.andReturn().getResponse().getHeader("Authorization");
// then
actions.andExpect(status().isOk()); // 상태 코드 검증
if (respJwt != null) {
assertTrue(respJwt.contains("Bearer "));
}
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.userId").value(3));
actions.andExpect(jsonPath("$.response.email").value("bunwuseok@example.com"));
actions.andExpect(jsonPath("$.response.myName").value("변우식"));
actions.andExpect(jsonPath("$.response.nickName").value("bun"));
actions.andExpect(jsonPath("$.response.photo.photoId").value(6));
actions.andExpect(jsonPath("$.response.photo.photoPath").exists());//uuid같은 경우 값이 계속 바뀌기 때문에 존재하는지만 체크
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void user_profile_update_fail_test() throws Exception {
// given
Integer userId = 3;
UserRequest.ProfileUpdateDTO reqDTO = new UserRequest.ProfileUpdateDTO();
reqDTO.setMyName("a"); // Invalid myName
reqDTO.setNickName("bunㅇㄹㅇㄹㅇㄹㄹㅇㅇㅇ");
reqDTO.setPassword("12345");
UserRequest.ProfileUpdateDTO.PhotoDTO photoDTO = new UserRequest.ProfileUpdateDTO.PhotoDTO();
photoDTO.setName("uuid_사용자사진3");
photoDTO.setBase64("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9");
reqDTO.setPhoto(photoDTO);
String reqBody = om.writeValueAsString(reqDTO);
System.out.println(reqBody);
// when
ResultActions actions = mvc.perform(
put("/user/profile/" + userId)
.header("Authorization", "Bearer " + jwt)
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// then
actions.andExpect(status().isBadRequest()); // Status code validation
actions.andExpect(jsonPath("$.status").value(400));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.response").doesNotExist());
actions.andExpect(jsonPath("$.errorMessage").value("실명은 최소 2자 이상 최대 20자 이하여야 합니다. : myName"));
}
@Test
public void user_profile_update_valid_fail_test() throws Exception {
// given
Integer userId = 3;
UserRequest.ProfileUpdateDTO reqDTO = new UserRequest.ProfileUpdateDTO();
reqDTO.setMyName("됐다됐다"); // Invalid myName
reqDTO.setNickName("bun");
reqDTO.setPassword("12");
UserRequest.ProfileUpdateDTO.PhotoDTO photoDTO = new UserRequest.ProfileUpdateDTO.PhotoDTO();
photoDTO.setName("uuid_사용자사진3");
photoDTO.setBase64("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9");
reqDTO.setPhoto(photoDTO);
String reqBody = om.writeValueAsString(reqDTO);
// when
ResultActions actions = mvc.perform(
put("/user/profile/" + userId)
.header("Authorization", "Bearer " + jwt)
.content(reqBody)
.contentType(MediaType.APPLICATION_JSON)
);
// then
actions.andExpect(status().isBadRequest());
actions.andExpect(jsonPath("$.status").value(400));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("비밀번호는 최소 4자 이상 최대 20자 이하여야 합니다. : password"));
}
//자동 로그인
@Test
public void app_auto_login_success_test() throws Exception {
// given
// when
ResultActions actions = mvc.perform(
post("/app/auto/login")
.header("Authorization", "Bearer " + jwt)
.contentType(MediaType.APPLICATION_JSON)
);
//eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println(respBody);
String respJwt = actions.andReturn().getResponse().getHeader("Authorization");
// then
actions.andExpect(status().isOk()); // 상태 코드 검증
if (respJwt != null) {
assertTrue(respJwt.contains("Bearer "));
}
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.id").value(3));
actions.andExpect(jsonPath("$.response.email").value("bunwuseok@example.com"));
actions.andExpect(jsonPath("$.response.photo").exists());
actions.andExpect(jsonPath("$.errorMessage").doesNotExist());
}
@Test
public void app_auto_login_fail_test() throws Exception {
// given
String invalidJwt = "";
// when
ResultActions actions = mvc.perform(
post("/app/auto/login")
.header("Authorization", "Bearer " + invalidJwt)
.contentType(MediaType.APPLICATION_JSON)
);
//eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println(respBody);
String respJwt = actions.andReturn().getResponse().getHeader("Authorization");
// then
// actions.andExpect(status().isOk()); // 상태 코드 검증
// if (respJwt != null) {
// assertTrue(respJwt.contains("Bearer "));
// }
actions.andExpect(status().isUnauthorized()) // 상태 코드가 401(Unauthorized)인지 검증
.andExpect(content().string(containsString("토큰이 유효하지 않습니다."))); // 응답 본문에 특정 메시지가
}
}
package com.example.finalproject.domain.inquiry;
import com.example.finalproject._core.utils.AppJwtUtil;
import com.example.finalproject.domain.user.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class InquiryRestControllerTest {
private ObjectMapper om = new ObjectMapper();
@Autowired
private MockMvc mvc;
private static String jwt;
@BeforeAll
public static void setUp() {
jwt = AppJwtUtil.create(
User.builder()
.id(1)
.email("junghein@example.com")
.password("1234")
.myName("junghein")
.build());
}
@Test
public void inquiryDetail_success_test() throws Exception {
// given
int inquiryId = 1;
// when
ResultActions actions = mvc.perform(
get("/app/inquiries/" + inquiryId)
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody : " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.inquiryId").value(1));
actions.andExpect(jsonPath("$.response.userId").value(1));
actions.andExpect(jsonPath("$.response.title").value("상품 문의"));
actions.andExpect(jsonPath("$.response.content").value("상품이 반팔도 셔츠도 입고 되면 좋겠는데 혹시 안 되나요?."));
actions.andExpect(jsonPath("$.response.commentDTO.brandId").value(1));
actions.andExpect(jsonPath("$.response.commentDTO.brandName").value("SALOMON"));
actions.andExpect(jsonPath("$.response.commentDTO.comment").value("출시 예정 제품 있습니다! 1월 27일 11:00부로 상품 구매 가능합니다!"));
}
@Test
public void inquiryDetail_fail_test() throws Exception {
// given
int inquiryId = 999;
// when
ResultActions actions = mvc.perform(
get("/app/inquiries/" + inquiryId)
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody : " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(404));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.errorMessage").value("페이지를 찾을 수 업습니다."));
}
@Test
public void inquirySave_success_test() throws Exception {
// given
InquiryRequest.SaveDTO reqDTO = new InquiryRequest.SaveDTO();
reqDTO.setBrandId(3);
reqDTO.setTitle("상품 문의");
reqDTO.setContent("상품 문의 좀 할께요.");
String reqBody = om.writeValueAsString(reqDTO);
System.out.println("reqBody : " + reqBody);
// when
ResultActions actions = mvc.perform(
post("/app/inquiries")
.header("Authorization", "Bearer " + jwt)
.contentType(MediaType.APPLICATION_JSON)
.content(reqBody)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody : " + respBody);
// then
actions.andExpect(status().isOk());
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response.brandId").value(3));
actions.andExpect(jsonPath("$.response.title").value("상품 문의"));
actions.andExpect(jsonPath("$.response.content").value("상품 문의 좀 할께요."));
}
@Test
public void inquirySave_fail_test() throws Exception {
// given
InquiryRequest.SaveDTO reqDTO = new InquiryRequest.SaveDTO();
reqDTO.setBrandId(999);
reqDTO.setTitle("상품 문의");
reqDTO.setContent("상품 문의 좀 할께요.");
String reqBody = om.writeValueAsString(reqDTO);
System.out.println("reqBody : " + reqBody);
// when
ResultActions actions = mvc.perform(
post("/app/inquiries")
.header("Authorization", "Bearer " + jwt)
.contentType(MediaType.APPLICATION_JSON)
.content(reqBody)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody : " + respBody);
// then
actions.andExpect(status().isNotFound());
actions.andExpect(jsonPath("$.status").value(404));
actions.andExpect(jsonPath("$.success").value(false));
actions.andExpect(jsonPath("$.response").isEmpty());
}
@Test
public void inquiryPage_success_test() throws Exception {
// when
ResultActions actions = mvc.perform(
get("/app/inquiries-lists")
.header("Authorization", "Bearer " + jwt)
);
// eye
String respBody = actions.andReturn().getResponse().getContentAsString();
System.out.println("respBody : " + respBody);
// then
actions.andExpect(jsonPath("$.status").value(200));
actions.andExpect(jsonPath("$.success").value(true));
actions.andExpect(jsonPath("$.response[0].inquiryId").value(1));
actions.andExpect(jsonPath("$.response[0].isReplied").value(true));
actions.andExpect(jsonPath("$.response[0].title").value("상품 문의"));
actions.andExpect(jsonPath("$.response[0].content").value("상품이 반팔도 셔츠도 입고 되면 좋겠는데 혹시 안 되나요?."));
actions.andExpect(jsonPath("$.response[0].createdAt").value("2024-01-25 11:30"));
}
}
테스트 성공후






curl:os 명령어로 os에서 http통신 요청 할 수 있는 명령어
인터넷이 되는 모든 컴퓨터들이 localhost가 될 수 있다.
httpie:os명령어 $ echo(모니터 화면)

http와 httpie 파일들로 html 파일을 만들어 낼거다.

이 경로의 html파일을 만든다.

ifndef =c언어 조건문
toc = 정렬
include = c언어 문법으로 스니펫츠에 저장

뒤에 오는 메서드명은 스니펫 안에에 있는 메서드 명을 근거로 적어야됨

= 블로그 RestAPI
metacoding <getinthere@naver.com>
1.0, 05.20, 2024: AsciiDoc article template
ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]
:user: user-controller-test
:board: board-controller-test
:reply: reply-controller-test
:toc: left
:toclevels: 2
:source-highlighter: highlightjs
== 유저
=== 회원가입
==== 요청 예시
include::{snippets}/{user}/join_test/http-request.adoc[]
==== 응답 예시
include::{snippets}/{user}/join_test/http-response.adoc[]
=== 회원가입 유저네임 중복체크
==== 요청 예시
include::{snippets}/{user}/join_username_same_fail_test/http-request.adoc[]
==== 응답 예시
include::{snippets}/{user}/join_username_same_fail_test/http-response.adoc[]
== 게시글
=== 게시글 상세보기
==== 요청 예시
include::{snippets}/{board}/detail_fait_test/http-request.adoc[]
==== 응답 예시
include::{snippets}/{board}/detail_fait_test/http-response.adoc[]
=== 게시글 한건보기
==== 요청 예시
include::{snippets}/{board}/find-one_fail_test/http-request.adoc[]
==== 응답 예시
include::{snippets}/{board}/find-one_fail_test/http-response.adoc[]
== 댓글
=== 댓글 쓰기
==== 요청 예시
include::{snippets}/{reply}/save_suc_test/http-request.adoc[]
==== 응답 예시
include::{snippets}/{reply}/save_suc_test/http-response.adoc[]
여기 까지 하고 다시 굽기



plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
group = 'shop.mtcoding'
version = '1.0'
java {
sourceCompatibility = '21'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// 서드파티
implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation group: 'org.qlrm', name: 'qlrm', version: '4.0.1'
implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4'
// 기본
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
tasks.named('test') {
useJUnitPlatform()
}
jar {
enabled = false
}
ext {
set('snippetsDir', file("build/generated-snippets"))
}
// src/docs 이하면 *.adoc 파일을 테스트시에 찾아내서 html 파일을 생성해줌
tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
// jar에 api.html에 옮기는 코드
bootJar {
dependsOn asciidoctor
copy {
from "${asciidoctor.outputDir}"
into 'src/main/resources/static/docs' // /static/docs로 복사!
}
}
위 코드에 맞게 셋팅을 잘해주고 돌리는데

오류
제데로 설정하고 구웠는데도 안되면 일부로


2. 로컬 환경에서의 단일 jar 파일 빌드

설정(jar파일 하나만 굽는 설정)

jar 파일이 하나만 만들어야 하는 이유
통과하면 libs의 jar파일이 하나만 만들어져야되는데 그 이유는
배포를 자동화 할때 스크립트가 통일화 되니까
- 로컬 테스트
- 로컬 빌드
- 로컬 실행 이렇게 3가지가 검증 돼야된다.
- version 설정
로컬에서 jar 실행하는 법

버전 설정

이렇게 까지 하고 다시 굽고 다시 실행해본다.
3. 익셉션

4. yaml 설정
resources/application-dev.yml
server:
servlet:
encoding:
charset: utf-8
force: true
# url rewrite 문제 해결
session:
tracking-modes: cookie
port: 8080
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test;MODE=MySQL
username: sa
password:
h2:
console:
enabled: true
sql:
init:
data-locations:
- classpath:db/data.sql
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
format_sql: true
default_batch_fetch_size: 10
defer-datasource-initialization: true
open-in-view: false
logging:
level:
org.hibernate.orm.jdbc.bind: TRACE
shop.mtcoding.blog: DEBUG
resources/application-prod.yml
server:
servlet:
encoding:
charset: utf-8
force: true
# url rewrite 문제 해결
session:
tracking-modes: cookie
port: 5000
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${RDS_HOST}:3306/{database 이름}
username: ${RDS_USERNAME}
password: ${RDS_PASSWORD}
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
default_batch_fetch_size: 10
dialect: org.hibernate.dialect.MySQL8Dialect
open-in-view: false
logging:
level:
org.hibernate.orm.jdbc.bind: INFO
shop.mtcoding.blog: INFO
github action 테스트는 dev모드로 실제 배포시에는 prod 모드로 진행한다.
resources/application.yml
spring: profiles: active: - dev
5. Git hub Action 설정

깃 헙에서 action - publish java package with gradle로 설정

.github/workflows/deply.yml
name:{워크 플로우 이름}
on:
push:
branches:
- master
# https://github.com/actions/setup-java
# actions/setup-java@v2는 사용자 정의 배포를 지원하고 Zulu OpenJDK, Eclipse Temurin 및 Adopt OpenJDK를 기본적으로 지원합니다. v1은 Zulu OpenJDK만 지원합니다.
jobs:
build:
// 우분투 환경에서 빌드
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
//Java Development Kit(JDK) 21을 설정합니다. temurin 배포판을 사용합니다.
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: 21
distribution: 'temurin'
// 권한 설정
- name: Permission
run: chmod +x ./gradlew
// 빌드
- name: Build with Gradle
run: ./gradlew clean build
워크플로우:일련의 작업들을 자동으로 실행하기 위한 절차 또는 과정

이 상태로 git hub 에 푸쉬하면 action 에서 확인할 수 있다. 초록 버튼이 뜬다면 CI 가 완료된거다.
Share article