From 24bc4b2d9623488bd878a952bfa7d6bb704479cd Mon Sep 17 00:00:00 2001 From: Minseong Park <52368015+pminsung12@users.noreply.github.com> Date: Thu, 4 Jul 2024 00:49:06 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[BSVR-62]=20=EA=B0=9C=EB=B0=9C=EB=8B=A8?= =?UTF-8?q?=EA=B3=84=20ci=20github=20actions=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: build와 test 단계를 포함하는 개발단계 CI Publish Unit Test Results, JUnit Report Action, Store test results, Test Report Summary 포함. * fix: pr labeler 트리거 이벤트 한정 pr labeler가 테스트 결과에도 돌아서 수정. * build: github action workflow group 수정 * chore: concurrency 이슈 확인 * fix: 문제가 되는 concurrency 삭제 --------- Co-authored-by: EunjiShin --- .github/workflows/dev-ci.yaml | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/dev-ci.yaml diff --git a/.github/workflows/dev-ci.yaml b/.github/workflows/dev-ci.yaml new file mode 100644 index 00000000..68b7ca8a --- /dev/null +++ b/.github/workflows/dev-ci.yaml @@ -0,0 +1,71 @@ +name: Build And Test + +on: + push: + branches: + - dev + - main + pull_request: + branches: + - dev + - main + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "corretto" + + - name: Cache Gradle + 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: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build -x test --stacktrace --parallel + + - name: Run tests + run: ./gradlew test + + - name: Publish Unit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/test/TEST-*.xml' + + - name: JUnit Report Action + uses: mikepenz/action-junit-report@v3 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + + - name: Store test results + uses: actions/upload-artifact@v3 + if: always() + with: + name: test-results + path: '**/build/test-results/test/TEST-*.xml' + + - name: Test Report Summary + if: always() + run: | + echo '## Test Report Summary' >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ./gradlew test --console=plain || true + echo '```' >> $GITHUB_STEP_SUMMARY \ No newline at end of file From f0884443fea49dcd970d671b66eba1864f380396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EB=94=94?= <38103085+EunjiShin@users.noreply.github.com> Date: Thu, 4 Jul 2024 00:57:08 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[BSVR-55]=20P6spy=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=BF=BC=EB=A6=AC=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20(#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : 윈도우에서 makeGitHooksExecutable 에러 fix * build: p6spy 의존성 추가 * feat: application-jpa에 p6spy 로깅 추가 * feat: query formatter, config 추가 * feat: controller부터 추적되도록 stack trace filter 수정 * feat: 상수명 변경 --------- Co-authored-by: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> --- infrastructure/jpa/build.gradle.kts | 3 + .../org/depromeet/spot/jpa/common/.gitkeep | 0 .../spot/jpa/common/P6spySqlFormatter.java | 70 +++++++++++++++++++ .../spot/jpa/config/P6spyConfig.java | 17 +++++ .../src/main/resources/application-jpa.yaml | 5 ++ versions.properties | 2 + 6 files changed, 97 insertions(+) delete mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/.gitkeep create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java diff --git a/infrastructure/jpa/build.gradle.kts b/infrastructure/jpa/build.gradle.kts index 06299a39..72d73e5c 100644 --- a/infrastructure/jpa/build.gradle.kts +++ b/infrastructure/jpa/build.gradle.kts @@ -5,6 +5,9 @@ dependencies { // spring implementation("org.springframework.boot:spring-boot-starter-data-jpa:_") + // p6spy + implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:_") + // h2 - DB (또는 도커) 세팅 후 사라질 예정,, runtimeOnly("com.h2database:h2") diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/.gitkeep b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java new file mode 100644 index 00000000..67f75a3b --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java @@ -0,0 +1,70 @@ +package org.depromeet.spot.jpa.common; + +import java.util.Locale; + +import org.hibernate.engine.jdbc.internal.FormatStyle; +import org.springframework.context.annotation.Configuration; + +import com.p6spy.engine.logging.Category; +import com.p6spy.engine.spy.appender.MessageFormattingStrategy; + +@Configuration +public class P6spySqlFormatter implements MessageFormattingStrategy { + + private static String ROOT_PACKAGE = "org.depromeet.spot"; + private static String P6SPY_PACKAGE = "org.depromeet.spot.jpa.common.P6spySqlFormatter"; + + @Override + public String formatMessage( + int connectionId, + String now, + long elapsed, + String category, + String prepared, + String sql, + String url) { + sql = formatSql(category, sql); + return String.format( + "[%s] | took %d ms | connectionId %d | %s | %s", + category, elapsed, connectionId, filterStack(), formatSql(category, sql)); + } + + private String formatSql(String category, String sql) { + if (isNotEmpty(sql) && isStatement(category)) { + String trimmedSQL = sql.trim().toLowerCase(Locale.ROOT); + if (isDDL(trimmedSQL)) { + return FormatStyle.DDL.getFormatter().format(sql); + } + return FormatStyle.BASIC.getFormatter().format(sql); + } + return sql; + } + + // 일반적인 쿼리인지 판단 + // 트랜잭션 커밋, 롤백 등 쿼리가 아닌 작업은 포맷팅하지 않는다. + private boolean isStatement(String category) { + return Category.STATEMENT.getName().equals(category); + } + + private boolean isNotEmpty(String sql) { + return sql != null && !sql.trim().isEmpty(); + } + + private boolean isDDL(String sql) { + return (sql.startsWith("create")) || sql.startsWith("alter") || sql.startsWith("comment"); + } + + private String filterStack() { + StackTraceElement[] stackTraces = new Throwable().getStackTrace(); + StringBuilder sb = new StringBuilder(); + int order = 1; + + for (StackTraceElement element : stackTraces) { + String trace = element.toString(); + if (trace.startsWith(ROOT_PACKAGE) && !trace.contains(P6SPY_PACKAGE)) { + sb.append("\n\t\t").append(order++).append(".").append(trace); + } + } + return sb.toString(); + } +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java new file mode 100644 index 00000000..3209fa9b --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java @@ -0,0 +1,17 @@ +package org.depromeet.spot.jpa.config; + +import jakarta.annotation.PostConstruct; + +import org.depromeet.spot.jpa.common.P6spySqlFormatter; +import org.springframework.context.annotation.Configuration; + +import com.p6spy.engine.spy.P6SpyOptions; + +@Configuration +public class P6spyConfig { + + @PostConstruct + public void setLogMessageFormat() { + P6SpyOptions.getActiveInstance().setLogMessageFormat(P6spySqlFormatter.class.getName()); + } +} diff --git a/infrastructure/jpa/src/main/resources/application-jpa.yaml b/infrastructure/jpa/src/main/resources/application-jpa.yaml index 8f7afdca..c5b523ff 100644 --- a/infrastructure/jpa/src/main/resources/application-jpa.yaml +++ b/infrastructure/jpa/src/main/resources/application-jpa.yaml @@ -17,3 +17,8 @@ spring: console: enabled: true path: /h2-console + +decorator: + datasource: + p6spy: + enable-logging: true \ No newline at end of file diff --git a/versions.properties b/versions.properties index c0dbde06..c3ce1b2e 100644 --- a/versions.properties +++ b/versions.properties @@ -23,3 +23,5 @@ version.org.springframework.boot..spring-boot-starter-data-jpa=3.0.1 version.org.springdoc..springdoc-openapi-starter-webmvc-ui=2.5.0 +version.com.github.gavlyukovskiy..p6spy-spring-boot-starter=1.9.0 + From 4dd7bfb72ea10a9b62dcd9e2faf658d8a9385f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EB=94=94?= <38103085+EunjiShin@users.noreply.github.com> Date: Fri, 5 Jul 2024 00:44:03 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[BSVR-56]=20QueryDSL=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : 윈도우에서 makeGitHooksExecutable 에러 fix * build(queryDSL): queryDSL 의존성 추가 * feat: queryDSl configuration 추가 * feat: QueryDSL 샘플 코드 추가 * docs: swagger 수정 * docs: editorconfig 설정에서 indent 4space로 조정 --------- Co-authored-by: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> --- .editorconfig | 2 +- .../member/controller/MemberController.java | 15 +++++++++ infrastructure/jpa/build.gradle.kts | 7 ++++- .../spot/jpa/config/QueryDslConfig.java | 20 ++++++++++++ .../repository/MemberCustomRepository.java | 31 +++++++++++++++++++ .../repository/MemberRepositoryImpl.java | 9 ++++++ .../spot/usecase/port/in/MemberUsecase.java | 4 +++ .../usecase/port/out/MemberRepository.java | 4 +++ .../spot/usecase/service/MemberService.java | 7 +++++ versions.properties | 4 +++ 10 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/QueryDslConfig.java create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java diff --git a/.editorconfig b/.editorconfig index e41a4de5..4eab9d38 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ [*] charset = utf-8 end_of_line = lf -indent_size = 2 +indent_size = 4 indent_style = space insert_final_newline = false max_line_length = 100 diff --git a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java index f7b9c633..673abfb3 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java +++ b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java @@ -1,16 +1,21 @@ package org.depromeet.spot.application.member.controller; +import java.util.List; + import org.depromeet.spot.application.member.dto.request.MemberRequest; import org.depromeet.spot.application.member.dto.response.MemberResponse; import org.depromeet.spot.usecase.port.in.MemberUsecase; import org.springframework.http.HttpStatus; +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.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.val; @@ -31,4 +36,14 @@ public MemberResponse create(@RequestBody MemberRequest request) { val member = memberUsecase.create(request.name()); return MemberResponse.from(member); } + + @GetMapping + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "이름으로 Member 조회하는 API") + public List findByName( + @RequestParam("name") @Parameter(name = "name", description = "사용자 이름", required = true) + final String name) { + val memberList = memberUsecase.findByName(name); + return memberList.stream().map(MemberResponse::from).toList(); + } } diff --git a/infrastructure/jpa/build.gradle.kts b/infrastructure/jpa/build.gradle.kts index 72d73e5c..5a06ab8d 100644 --- a/infrastructure/jpa/build.gradle.kts +++ b/infrastructure/jpa/build.gradle.kts @@ -5,12 +5,17 @@ dependencies { // spring implementation("org.springframework.boot:spring-boot-starter-data-jpa:_") + // queryDSL + implementation("com.querydsl:querydsl-jpa:_:jakarta") + annotationProcessor("com.querydsl:querydsl-apt:_:jakarta") + annotationProcessor("jakarta.annotation:jakarta.annotation-api") + annotationProcessor("jakarta.persistence:jakarta.persistence-api") + // p6spy implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:_") // h2 - DB (또는 도커) 세팅 후 사라질 예정,, runtimeOnly("com.h2database:h2") - } tasks.bootJar { enabled = false } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/QueryDslConfig.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/QueryDslConfig.java new file mode 100644 index 00000000..5b64ca95 --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/QueryDslConfig.java @@ -0,0 +1,20 @@ +package org.depromeet.spot.jpa.config; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +@Configuration +public class QueryDslConfig { + + @PersistenceContext private EntityManager entityManager; + + @Bean + public JPAQueryFactory queryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java new file mode 100644 index 00000000..f0d490a7 --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java @@ -0,0 +1,31 @@ +package org.depromeet.spot.jpa.member.repository; + +import static org.depromeet.spot.jpa.member.entity.QMemberEntity.memberEntity; + +import java.util.List; + +import org.depromeet.spot.jpa.member.entity.MemberEntity; +import org.springframework.stereotype.Repository; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class MemberCustomRepository { + + private final JPAQueryFactory queryFactory; + + public List findByName(final String name) { + return queryFactory.selectFrom(memberEntity).where(eqMemberName(name)).fetch(); + } + + private BooleanExpression eqMemberName(final String name) { + if (name == null) { + return null; + } + return memberEntity.name.eq(name); + } +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java index 42507851..8c4ebba9 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java @@ -1,5 +1,7 @@ package org.depromeet.spot.jpa.member.repository; +import java.util.List; + import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.jpa.member.entity.MemberEntity; import org.depromeet.spot.usecase.port.out.MemberRepository; @@ -13,10 +15,17 @@ public class MemberRepositoryImpl implements MemberRepository { private final MemberJpaRepository memberJpaRepository; + private final MemberCustomRepository memberCustomRepository; @Override public Member save(Member member) { val memberEntity = memberJpaRepository.save(MemberEntity.from(member)); return memberEntity.toDomain(); } + + @Override + public List findByName(String name) { + val memberEntities = memberCustomRepository.findByName(name); + return memberEntities.stream().map(MemberEntity::toDomain).toList(); + } } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java index 79af200b..42b00e6e 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java @@ -1,8 +1,12 @@ package org.depromeet.spot.usecase.port.in; +import java.util.List; + import org.depromeet.spot.domain.member.Member; public interface MemberUsecase { Member create(String name); + + List findByName(String name); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java index b7b5fe40..58da7024 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java @@ -1,8 +1,12 @@ package org.depromeet.spot.usecase.port.out; +import java.util.List; + import org.depromeet.spot.domain.member.Member; public interface MemberRepository { Member save(Member member); + + List findByName(String name); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java index 061ec57c..3e04c948 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java @@ -1,5 +1,7 @@ package org.depromeet.spot.usecase.service; +import java.util.List; + import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.usecase.port.in.MemberUsecase; import org.depromeet.spot.usecase.port.out.MemberRepository; @@ -19,4 +21,9 @@ public Member create(final String name) { val member = new Member(null, name); return memberRepository.save(member); } + + @Override + public List findByName(final String name) { + return memberRepository.findByName(name); + } } diff --git a/versions.properties b/versions.properties index c3ce1b2e..5844e6f2 100644 --- a/versions.properties +++ b/versions.properties @@ -25,3 +25,7 @@ version.org.springdoc..springdoc-openapi-starter-webmvc-ui=2.5.0 version.com.github.gavlyukovskiy..p6spy-spring-boot-starter=1.9.0 +version.com.querydsl..querydsl-apt=5.0.0 + +version.com.querydsl..querydsl-jpa=5.0.0 + From eca804715be01058eec60bc325137bd7c3847ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EB=94=94?= <38103085+EunjiShin@users.noreply.github.com> Date: Fri, 5 Jul 2024 00:48:11 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[BSVR-64]=20PR=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=88=98=EC=A0=95=20&=20Issue=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=B6=94=EA=B0=80=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : 윈도우에서 makeGitHooksExecutable 에러 fix * docs(PR): PR 템플릿에 실행 화면 섹션 추가 * docs(issue): Issue 템플릿 추가 --------- Co-authored-by: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug.md | 19 +++++++++++++++++++ .github/ISSUE_TEMPLATE/suggest.md | 21 +++++++++++++++++++++ .github/pull_request_template.md | 8 +++++++- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.md create mode 100644 .github/ISSUE_TEMPLATE/suggest.md diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 00000000..1d50654b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,19 @@ +--- +name: 버그 레포트 +about: 버그 제보 & QA +title: "[BUG] " +labels: 🐛 Bug +assignees: "" +--- + +## 📍 재현 위치 + + + +## 📌 상황 설명 + + + +## 🤔 정상 기대값 (선택) + + diff --git a/.github/ISSUE_TEMPLATE/suggest.md b/.github/ISSUE_TEMPLATE/suggest.md new file mode 100644 index 00000000..374d110b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/suggest.md @@ -0,0 +1,21 @@ +--- +name: Suggest +about: 프로젝트 관련해서 무엇이든 제안해주세요! +title: "[제안]" +labels: 👋 Suggest +assignees: "" +--- + +## 💁‍♀️ 제안 사항 + + + + +## 👀 제안 이유 + + + + +## ✅ 참고 사항 + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bd87027b..fbbb509c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -18,4 +18,10 @@ ## 🌱 연관 내용 (선택) -- 관련 있는 내용, 또는 앞으로 고려할 내용을 적어주세요. +- 관련 있는 내용, 또는 앞으로 고려할 내용을 적어주세요. + +
+ +## 💻 실행 화면 (필수) + +- 실행 결과 사진을 첨부해주세요.