-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add: 주문하기 코드를 옮겨온다. 옮겨온다. * refactor: gitignore 변경 * remove: 동시성 테스트 진행으로 인해 발생하는 build 과부하 방지를 위해 동시성테스트 제거 동시성 테스트 제거 * add: 기존 동시성테스트 추가 동시성 테스트 추가 * add: ci-cd 파일 추가 ci-cd 파일 추가 * rename: workflows -> workflow * rename: ci-cd to gradle * Create gradle.yml * remove: workflow remove * add: CI with java * refactor: 자동으로 application.yml 파일 생성하도록 추가 * add: make application.yml 설정파일 생성 * rename: application 설정파일 이름 재설정 * remove: 동시성 테스트 삭제 * change: setup jdk * remove: deploy 코드 제거 * add: make application.yml 방식 추가 * refactor: application.yml 만드는 코드 수정 * add: deploy gradle 추가 * rename: secret deploy server로 ip 보호 * add: EC2 서버에 키를 알려줌 * add: 호스트키 신뢰하도록 설정 * rename: 호스트키 이름 변경 * add: HOST 추가 * add: 호스트키 검증 무시 * add: redirect-token-uri를 따로 관리해서 배포환경과 다른값 설정할 수 있도록 함 * refactor: 이름 명시 * refactor: 배포 코드 수정 * refactor: 절대 경로 설정 * add: 배포시 중간에 sleep 10 추가 * remove: 그냥 실행되는 부분 제거 * add: 기존 실행중인 8080포트 죽이기 * refactor: 절대경로로 설정
- Loading branch information
1 parent
68235b7
commit 531a67c
Showing
98 changed files
with
4,443 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
name: JAVA CI with GRADLE | ||
|
||
on: | ||
push: | ||
branches: [ "yunjunghun0116" ] | ||
pull_request: | ||
branches: [ "yunjunghun0116" ] | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: 시작 | ||
uses: actions/checkout@v4 | ||
|
||
- name: JDK 21로 실행 | ||
uses: actions/setup-java@v4 | ||
with: | ||
java-version: '21' | ||
distribution: 'temurin' | ||
cache: gradle | ||
|
||
- name: gradle 생성 | ||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 | ||
|
||
- name: 설정파일 생성 | ||
run: | | ||
touch ./src/main/resources/application.yml | ||
echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml | ||
cat ./src/main/resources/application.yml | ||
- name: gradlew 권한 추가 | ||
run: chmod +x gradlew | ||
|
||
- name: 빌드 작업 | ||
run: ./gradlew build | ||
|
||
- name: 테스트 작업 | ||
run: ./gradlew test | ||
|
||
- name: 서버에 배포하기 | ||
env: | ||
SSH_KEY: ${{ secrets.SSH_KEY }} | ||
DEPLOY_SERVER: ${{ secrets.DEPLOY_SERVER }} | ||
DEPLOY_USER: ${{ secrets.DEPLOY_USER }} | ||
run: | | ||
echo "$SSH_KEY" > key.pem | ||
chmod 400 key.pem | ||
scp -o StrictHostKeyChecking=no -i key.pem build/libs/*.jar $DEPLOY_USER@$DEPLOY_SERVER:~/spring-gift-point/build/libs/ | ||
scp -o StrictHostKeyChecking=no -i key.pem ./src/main/resources/application.yml $DEPLOY_USER@$DEPLOY_SERVER:~/spring-gift-point/src/main/resources/application.yml | ||
ssh -o StrictHostKeyChecking=no -i key.pem $DEPLOY_USER@$DEPLOY_SERVER " | ||
sudo lsof -t -i:8080 | xargs -r sudo kill -9 | ||
nohup java -jar ~/spring-gift-point/build/libs/spring-gift-0.0.1-SNAPSHOT.jar &" | ||
dependency-submission: | ||
|
||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Set up JDK 21 | ||
uses: actions/setup-java@v4 | ||
with: | ||
java-version: '21' | ||
distribution: 'temurin' | ||
|
||
- name: Generate and submit dependency graph | ||
uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,64 @@ | ||
# spring-gift-point | ||
# spring-gift-point | ||
|
||
### 과제 진행 요구 사항 | ||
|
||
- 미션은 [포인트](https://github.com/kakao-tech-campus-2nd-step2/spring-gift-point) 저장소를 포크하고 클론하는 것으로 시작한다. | ||
- [온라인 코드 리뷰 요청 1단계 문서](https://github.com/next-step/nextstep-docs/blob/master/codereview/review-step1.md)를 참고하여 실습 환경을 | ||
구축한다. | ||
- 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다. | ||
- Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 | ||
추가한다. [AngularJS Git Commit Message Convention](https://gist.github.com/stephenparish/9941e89d80e2bc58a153)을 참고해 커밋 | ||
메시지를 작성한다. | ||
|
||
### 프로그래밍 요구 사항 | ||
|
||
- 자바 코드 컨벤션을 지키면서 프로그래밍 한다. (들여쓰기는 '4 spaces' 로 한다) | ||
- indent (들여쓰기) depth 를 3이 넘지 않도록 구현한다. | ||
- 3항 연산자를 사용하지 않는다. | ||
- 함수는 한가지 일만 하도록 최대한 작게 만든다. | ||
- 함수의 길이가 15 라인을 넘어가지 않도록 구현한다. | ||
- JUnit 5 와 AssertJ 를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. | ||
- else 예약어를 사용하지 않는다. | ||
- 도메인 로직에 단위 테스트를 구현해야 한다.(핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다.) | ||
|
||
### 기능 요구 사항 (5주차) | ||
|
||
#### 0단계 | ||
|
||
- [X] 주문하기 코드를 옮겨온다. | ||
|
||
### 나만의 HTTP RULE | ||
|
||
| HTTP Method | 사용상황 | 반환(상태코드) | | ||
|-------------|--------------------------------|----------| | ||
| GET | 리소스의 조회 | 200 | | ||
| POST | 새로운 리소스 생성 | 201 | | ||
| PUT | 리소스의 전체 업데이트 또는 ID를 통한 리소스 생성 | 204 | | ||
| PATCH | 리소스의 일부분(일부 필드) 업데이트 | 204 | | ||
| DELETE | 리소스의 삭제 | 204 | | ||
|
||
### 나만의 계층 RULE | ||
|
||
| 계층 | 역할 | | ||
|------------|-------------------------------------------------------------| | ||
| Controller | HTTP 요청을 받아 적절한 Service 호출, 입력 검증, 유효성 검사, HTTP 응답 생성 및 반환 | | ||
| Service | 비즈니스 로직 수행, DTO 와 엔티티 변환, 다수 Repository 를 통한 하나의 트랜잭션 처리 작업 | | ||
| Model | Entity, DTO 가 속하며 데이터구조, 데이터베이스와의 연동되는 객체 | | ||
| Repository | DB 관련 CRUD 작업, DB 의 결과를 Entity 로 변환하는 작업 | | ||
|
||
### 나만의 개행 RULE | ||
|
||
- 지역변수는 사이에 개행을 두지 않는다. 하지만 첫 지역변수 전줄, 마지막 지역변수 다음줄에 개행을 추가한다. | ||
- 생성자 전후에 개행을 추가한다. | ||
- 추상체, 구현체 모두 메서드 전후에 개행을 추가한다. 단 마지막 메서드 후에는 추가하지 않는다. | ||
- 클래스의 마지막 줄에는 개행을 추가한다. | ||
|
||
### 연관관계 매핑 RULE | ||
|
||
- M:1 관계에서는 M 에서 1 에 대한 정보까지 추가한다. ex) setProduct() 는 Product 가 아닌 WishProduct 에서 수행을 하는것 처럼 | ||
- save 를 하는 과정에서 우선 객체를 생성하고 연관관계를 맺어준 후에 repository.save() 를 호출한다. | ||
|
||
### 계층간 의존 RULE | ||
|
||
- M:1 관계에서 M 에서는 1에 대한 조회만을 수행하기에 서비스 계층에서는 레포지토리 계층을 의존한다.(R) | ||
- M:1 관계에서 1 에서는 M에 대한 로직을 수행할 수 있기에(삭제 등) 서비스 계층을 의존한다.(CUD) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package gift.client; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import gift.config.properties.KakaoProperties; | ||
import gift.dto.giftorder.GiftOrderResponse; | ||
import gift.dto.kakao.KakaoAuthResponse; | ||
import gift.dto.kakao.KakaoTokenResponse; | ||
import gift.dto.kakao.template.KakaoTemplate; | ||
import gift.dto.kakao.template.KakaoTemplateCommerce; | ||
import gift.dto.kakao.template.KakaoTemplateContent; | ||
import gift.dto.kakao.template.KakaoTemplateLink; | ||
import gift.exception.BadRequestException; | ||
import gift.exception.InvalidKakaoTokenException; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.web.client.RestClient; | ||
|
||
import java.net.URI; | ||
|
||
@Component | ||
public class KakaoApiClient { | ||
|
||
private final RestClient restClient; | ||
private final KakaoProperties kakaoProperties; | ||
private final ObjectMapper objectMapper = new ObjectMapper(); | ||
private static final String INVALID_TOKEN_MESSAGE = "유효하지 않은 토큰입니다. 갱신이 필요합니다."; | ||
private static final String TOKEN_BASE_URL = "https://kauth.kakao.com/oauth/token"; | ||
|
||
public KakaoApiClient(KakaoProperties kakaoProperties, RestClient restClient) { | ||
this.kakaoProperties = kakaoProperties; | ||
this.restClient = restClient; | ||
} | ||
|
||
public KakaoTokenResponse getTokenResponse(String code, String redirectUri) { | ||
var body = new LinkedMultiValueMap<String, String>(); | ||
body.add("grant_type", "authorization_code"); | ||
body.add("client_id", kakaoProperties.restApiKey()); | ||
body.add("redirect_uri", redirectUri); | ||
body.add("code", code); | ||
|
||
var response = restClient.post() | ||
.uri(URI.create(TOKEN_BASE_URL)) | ||
.contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||
.body(body) | ||
.retrieve() | ||
.body(String.class); | ||
|
||
return convertDtoWithJsonString(response, KakaoTokenResponse.class); | ||
} | ||
|
||
public KakaoTokenResponse getRefreshedTokenResponse(String refreshToken) { | ||
var body = new LinkedMultiValueMap<String, String>(); | ||
body.add("grant_type", "refresh_token"); | ||
body.add("client_id", kakaoProperties.restApiKey()); | ||
body.add("refresh_token", refreshToken); | ||
|
||
var response = restClient.post() | ||
.uri(URI.create(TOKEN_BASE_URL)) | ||
.contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||
.body(body) | ||
.retrieve() | ||
.onStatus(statusCode -> statusCode.equals(HttpStatus.UNAUTHORIZED), (req, res) -> { | ||
throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); | ||
}) | ||
.onStatus(statusCode -> statusCode.equals(HttpStatus.BAD_REQUEST), (req, res) -> { | ||
throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); | ||
}) | ||
.body(String.class); | ||
|
||
return convertDtoWithJsonString(response, KakaoTokenResponse.class); | ||
} | ||
|
||
public KakaoAuthResponse getKakaoAuthResponse(KakaoTokenResponse kakaoTokenResponse) { | ||
var url = "https://kapi.kakao.com/v2/user/me"; | ||
var header = "Bearer " + kakaoTokenResponse.accessToken(); | ||
|
||
var response = restClient.get() | ||
.uri(URI.create(url)) | ||
.header("Authorization", header) | ||
.retrieve() | ||
.body(String.class); | ||
|
||
return convertDtoWithJsonString(response, KakaoAuthResponse.class); | ||
} | ||
|
||
public void sendSelfMessageOrder(String accessToken, GiftOrderResponse giftOrderResponse) { | ||
try { | ||
var url = "https://kapi.kakao.com/v2/api/talk/memo/default/send"; | ||
var header = "Bearer " + accessToken; | ||
|
||
var template = getCommerceTemplate(giftOrderResponse); | ||
var body = new LinkedMultiValueMap<String, Object>(); | ||
body.add("template_object", objectMapper.writeValueAsString(template)); | ||
|
||
restClient.post() | ||
.uri(URI.create(url)) | ||
.header("Authorization", header) | ||
.contentType(MediaType.APPLICATION_FORM_URLENCODED) | ||
.body(body) | ||
.retrieve() | ||
.onStatus(statusCode -> statusCode.equals(HttpStatus.UNAUTHORIZED), (req, res) -> { | ||
throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); | ||
}) | ||
.body(String.class); | ||
} catch (JsonProcessingException exception) { | ||
throw new BadRequestException("잘못된 입력으로 인해 JSON 파싱에 실패했습니다" + exception.getMessage()); | ||
} | ||
} | ||
|
||
private <T> T convertDtoWithJsonString(String response, Class<T> returnTypeClass) { | ||
try { | ||
return objectMapper.readValue(response, returnTypeClass); | ||
} catch (JsonProcessingException exception) { | ||
throw new RuntimeException(returnTypeClass.getName() + "의 데이터를 DTO 로 변환하는 과정에서 예외가 발생했습니다.", exception); | ||
} | ||
} | ||
|
||
private KakaoTemplate getCommerceTemplate(GiftOrderResponse giftOrderResponse) { | ||
var objectType = "commerce"; | ||
var link = new KakaoTemplateLink("https://gift.kakao.com/product/2370524"); | ||
var content = new KakaoTemplateContent(giftOrderResponse.message(), "https://img1.kakaocdn.net/thumb/[email protected]/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240417111629_616eccb9d4cd464fa06d3430947dce15.jpg", giftOrderResponse.message(), link); | ||
var commerce = new KakaoTemplateCommerce(giftOrderResponse.optionInformation().productName() + "[" + giftOrderResponse.optionInformation().name() + "]", giftOrderResponse.optionInformation().price() * giftOrderResponse.quantity()); | ||
return new KakaoTemplate(objectType, content, commerce); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package gift.config; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.client.SimpleClientHttpRequestFactory; | ||
import org.springframework.web.client.RestClient; | ||
|
||
@Configuration | ||
public class RestClientConfig { | ||
|
||
@Bean | ||
public RestClient restClient() { | ||
var factory = new SimpleClientHttpRequestFactory(); | ||
factory.setConnectTimeout(5000); | ||
factory.setReadTimeout(5000); | ||
return RestClient.builder() | ||
.requestFactory(factory) | ||
.build(); | ||
} | ||
} |
Oops, something went wrong.