Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ngBoot into fix/#105/JPA
  • Loading branch information
Youngseo-Jeon0313 committed Apr 9, 2023
2 parents d3ef167 + 5641aaf commit bb6165e
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 104 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Spring Boot & Gradle & Docker & EC2 CI/CD

on:
push:
branches:
- main


jobs:
build:
runs-on: ubuntu-latest #ubuntu-20.04
steps:
# 기본 체크아웃
- name: Checkout
uses: actions/checkout@v3
## 1) git ignore에 등록된 application.yml 파일 생성해주기
- name: make application.yml
run: |
cd ./src/main/resources
touch ./application.yml
echo "${{ secrets.YML }}" > ./application.yml
shell: bash

## 2) 스프링 프로젝트 jar 파일 빌드
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew clean build --no-daemon
## 3) Docker Hub에 이미지 push 하기
- name: Docker build
run: |
docker login -u ${{ secrets.USERNAME }} -p ${{ secrets.PASSWORD }}
docker build -t ${{ secrets.USERNAME }}/text-in-the-road-server:0.1 .
docker push ${{ secrets.USERNAME }}/text-in-the-road-server:0.1
## 4) Docker Hub에 Push한 이미지를 리눅스 서버에 받아와서 run
- name: Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.AWS_TEST_X_HOST }}
username: ${{ secrets.AWS_TEST_X_USERNAME }}
key: ${{ secrets.AWS_TEST_X_KEY }}
envs: GITHUB_SHA
script: |
docker login -u ${{ secrets.USERNAME }} -p ${{ secrets.PASSWORD }}
docker rm -f $(docker ps -qa)
docker pull ${{ secrets.USERNAME }}/text-in-the-road-server:0.1
docker run -d -p 8080:8080 ${{ secrets.USERNAME }}/text-in-the-road-server:0.1
185 changes: 92 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,94 @@
# S3 연동
일단 저는
https://velog.io/@do-hoon/게시글-생성-파일-업로드SpringBoot-JPA-AWS-S3
위의 블로그를 참고하였습니다.

## 1. build.gradle 설정
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation "com.amazonaws:aws-java-sdk-s3"

위의 두가지를 설정해 주면 됩니다.

## 2. application.yml 설정
cloud:
aws:
credentials:
accessKey: SLKFJ # AWS IAM AccessKey 적기
secretKey: SDLKFJSLKDF # AWS IAM SecretKey 적기
s3:
bucket: com.gleonroad.umcproject
region:
static: ap-northeast-2
auto: false
stack:
auto: false


accessKey와 secretKey는 aws에서 설정한 값을 넣으면 됩니다.
해당 값은 공개하면 안되니 github에는 올리지 않는게 좋다고 합니다.
위에 적어놓은 값은 임의 값입니다.
## 2-1. application.yml 추가 설정
~~~
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/umc3project;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driver-class-name: org.h2.Driver
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
#swagger? ?? ???
mvc:
pathmatch:
matching-strategy: ant_path_matcher
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
show_sql: true
format_sql: true
default_batch_fetch_size: 100
logging:
level:
org.hibernate.SQL: debug
org.hibernate.type: trace
com:
amazonaws:
util:
EC2MetadataUtils: ERROR
cloud:
aws:
credentials:
accessKey: SLDKFJ # AWS IAM AccessKey 적기
secretKey: SLDKFJSK # AWS IAM SecretKey 적기
s3:
bucket: com.gleonroad.umcproject
region:
static: ap-northeast-2
auto: false
stack:
auto: false
~~~
위에 servlet 설정과 logging 설정을 해줘야 합니다.

## 3. S3Uploader 생성

S3Uploader.java 파일을 그대로 쓰시면 됩니다.
------------
저 같은 경우 하나의 forum 글에 다수의 이미지나 비디오가 들어갈 수 있어서
forumImage라는 entity를 따로 만들었습니다.
forum 과 forumImage 는 1:다 관계입니다.

s3에 이미지를 업로드 하는 로직은
forum글 생성시 이미지가 있으면 s3에 업로드 하면서 반환되는 이미지 url를 저장합니다.
코드는 ForumController에서 createForum api에 있습니다.

# s3 설정은 하나로 통일하면 될 것 같습니다.
# server tech

![image](https://ifh.cc/g/zA1y2W.jpg)


## ec2 사용한 무중단 배포

<aside>
📌 Amazon RDS로 MySQL 배포

</aside>

Amazon RDS를 사용해 클라우드에서 MySQL을 배포해 프로젝트 기간 동안에 작업을 진행했습니다.

Amazon RDS를 사용하면 비용 효율적이고 크기 조정 가능한 하드웨어 용량을 갖춘 확장 가능한 MySQL 서버를 몇 분 만에 배포할 수 있습니다.

- Java Mail Library: SMTP를 사용하여 메일을 전송하는 방법으로 신고 기능과 비밀번호 재발급 기능을 구현했습니다.
- Amazon S3: 저장 용량이 무한대이고 파일 저장에 최적화되어 있는 S3를 이미지 업로드를 위해 사용했습니다.

![image](https://user-images.githubusercontent.com/81344634/218734889-8d702a3b-7841-4e77-8358-48710769879c.png)


<aside>
📌 스프링 부트 애플리케이션을 AWS EC2에 도커를 통해 배포

</aside>

AWS EC2 인스턴스를 생성하고, 도커를 설치한 후 애플리케이션을 배포 합니다.

EC2의 private IP를 RDS의 인바운드규칙 소스에 추가해 RDS의 MySQL과 연동 상태를 유지합니다

![image](https://user-images.githubusercontent.com/81344634/218735003-5e35d2e9-4eb7-4625-8ff8-fd3389ed47a7.png)

## swagger 사용

swagger 사용하여 애플리케이션의 **RESTful API** 문서를 자동으로 생성하고 test case를 만들어서 사용하였습다. 이를 사용하여 application의 모든 엔드포인트를 살펴보고 test를 해보았습니다.

```java
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.umc3_teamproject"))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Web Application Api")
.description("설명 부분")
.version("1.0.0")
.build();
}
}
```

## Social Login 사용

<aside>
📌 Apple Login

</aside>

- token 포함 필요한 정보를 모두 받아 jwt로 만든 후 서버로 넘겨 DB에 저장한다.
- 필요한 정보만 payload 에 넣어서 jwt 를 다시 만들고 클라이언트에게 전달한다.
- reject 사유를 피하기 위해 꼭 필요한 기능이다.

<aside>
📌 KaKao Login

</aside>

- Client에서 access token을 받고 보안상 jwt로 만든 후 서버로 넘긴다
- token을 검증 후 token을 이용하여 사용자 정보를 얻는다.

## TDD ****(Test-Driven-Development)**** 방법론 사용

> 테스트 코드를 작성하며 아래와 같은 과정으로 개발을 진행했습니다.
>
1. 테스트 코드 작성
2. 테스트 코드를 통과하는 구현 코드 작성
3. 테스트가 통과할 경우 리팩토링 (더욱 자세한 테스트, 통과할 수 있는 코드, 가시성 등)

> 이러한 개발 과정을 통해 다음과 같은 이점을 얻을 수 있었습니다
>
1. 단기적인 뚜렷한 목표를 세울 수 있다.
2. 추가 구현의 용이함 - 새로운 기능들이 기존의 코드에 영향을 미치는지 알 수 있다.
3. 운영 단계가 아닌, 배포 단계에서 버그를 검출해 내어 불필요한 비용을 감축할 수 있을 것이다. (커뮤니케이션, 시간 등)
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'

implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.projectlombok:lombok'

implementation 'junit:junit:4.13.2'
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.8.0'
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ services:

spring:
container_name: spring
image: jeonyoungseo/text-in-the-road-server
image: jeonyoungseo/text-in-the-road-server:0.1
expose:
- 9000
- 8080
ports:
- 8080:9000
- 8080:8080
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import com.example.umc3_teamproject.config.resTemplate.ResponseException;
import com.example.umc3_teamproject.domain.dto.request.ScriptRequestDto;
import com.example.umc3_teamproject.domain.dto.response.ScriptResponseDto;
import com.example.umc3_teamproject.domain.item.Paragraph;
import com.example.umc3_teamproject.domain.item.Script;
import com.example.umc3_teamproject.repository.ScriptRepository;
import com.example.umc3_teamproject.service.JwtService;
import com.example.umc3_teamproject.service.ParagraphService;
import com.example.umc3_teamproject.service.ScriptService;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
Expand All @@ -34,7 +37,10 @@
@Api(tags = {"Script Api"})
public class ScriptController {

@Autowired
private final ScriptService scriptService;
@Autowired
private final ParagraphService paragraphService;
private final ScriptRepository scriptRepository;
private final ScriptResponseDto scriptResponseDto;
private final JwtService jwtService;
Expand All @@ -48,12 +54,25 @@ public ResponseEntity<?> writeScript(@RequestBody ScriptRequestDto.Register scri
public ResponseEntity<?> readScriptById(@PathVariable("id") Long id) {

// 참고 문헌: https://jogeum.net/9
/*
Optional<Script> optionalProduct=scriptRepository.findById(id);
if (optionalProduct.isPresent()) {
Script script1 = optionalProduct.get();
return scriptResponseDto.successScript(script1);
}
return null;
*/

List<Paragraph> paragraphList=paragraphService.findByScriptId(id);
//model.addAttribute("scriptList", scriptList);

Map<String, Object> result=new HashMap<>();
result.put("scripts", paragraphList);
result.put("count", paragraphList.size());

return ResponseEntity.ok().body(result);

}

@GetMapping("/member/me")
Expand All @@ -72,6 +91,21 @@ public ResponseEntity<?> findScriptByUser() throws ResponseException {

}

@GetMapping("/script/{scriptId}")
public ResponseEntity<?> findParagraphByScript(@PathVariable("scriptId") Long scriptId) {
// @PageableDefault(page=0, size=10, sort="id", direction = Sort.Direction.DESC) Pageable pageable) {

List<Paragraph> paragraphList=paragraphService.findByScriptId(scriptId);
//model.addAttribute("scriptList", scriptList);

Map<String, Object> result=new HashMap<>();
result.put("scripts", paragraphList);
result.put("count", paragraphList.size());

return ResponseEntity.ok().body(result);

}

@GetMapping("/all")
public ResponseEntity<Map<String, Object>> getScriptListAll() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ private static class Body {
private String title;
//private String type;
private String contents;
private int count;
private List<Paragraph> paragraphList;

private LocalDateTime createdDate;
Expand All @@ -45,6 +46,7 @@ public ResponseEntity<?> firstSuccess(Script script) {
.scriptId(script.getScriptId())
.title(script.getTitle())
.contents("no contents")
.count(0)
.createdDate(script.getCreatedDate())
.modifiedDate(script.getModifiedDate())
.build();
Expand All @@ -55,6 +57,7 @@ public ResponseEntity<?> successScript(Script script) {

List<Paragraph> paragraphList = new ArrayList<>();
paragraphList=script.getParagraphList();
int paragraph_length=paragraphList.size();

//Member script_member=memberRepository.getUser(script.getMemberId());

Expand All @@ -74,6 +77,7 @@ public ResponseEntity<?> successScript(Script script) {
.scriptId(script.getScriptId())
.title(script.getTitle())
.contents(firestScriptContent)
.count(paragraph_length)
.paragraphList(script.getParagraphList())
.createdDate(script.getCreatedDate())
.modifiedDate(script.getModifiedDate())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;


@Service
Expand Down Expand Up @@ -57,15 +58,14 @@ public Paragraph updateParagraph(Long id, String title) {
return toChangeParagraph;
}





@Transactional
public void saveItem(Paragraph paragraph) {

paragraphRepository.save(paragraph);


}

public List<Paragraph> findByScriptId(Long scriptId){
return em.createQuery("select s from Paragraph s where s.scriptId.id= :id", Paragraph.class)
.setParameter("id", scriptId)
.getResultList();
};
}

0 comments on commit bb6165e

Please sign in to comment.