Skip to content

Commit

Permalink
[MERGE] exception 위치 변경에 따른 충돌 해결
Browse files Browse the repository at this point in the history
  • Loading branch information
unanchoi committed Apr 6, 2024
2 parents 4e86c2c + 8c27e4d commit 1dc8c85
Show file tree
Hide file tree
Showing 89 changed files with 688 additions and 456 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

- name: Create FireBase JSON file From AWS
run: |
aws s3 cp --region ap-northeast-2 s3://${{ secrets.AWS_BUCKET_NAME }}/json/smeem_fcm.json src/main/resources/smeem_fcm.json
aws s3 cp --region ap-northeast-2 s3://${{ secrets.AWS_BUCKET_NAME }}/json/smeem_fcm.json smeem-external/src/main/resources/firebase/smeem_fcm.json
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

- name: Create FireBase JSON file From AWS
run: |
aws s3 cp --region ap-northeast-2 s3://${{ secrets.AWS_PROD_BUCKET_NAME }}/json/smeem_fcm.json src/main/resources/smeem_fcm.json
aws s3 cp --region ap-northeast-2 s3://${{ secrets.AWS_PROD_BUCKET_NAME }}/json/smeem_fcm.json smeem-external/src/main/resources/firebase/smeem_fcm.json
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

- name: Create FireBase JSON file From AWS
run: |
aws s3 cp --region ap-northeast-2 s3://${{ secrets.AWS_BUCKET_NAME }}/json/smeem_fcm.json src/main/resources/smeem_fcm.json
aws s3 cp --region ap-northeast-2 s3://${{ secrets.AWS_BUCKET_NAME }}/json/smeem_fcm.json smeem-external/src/main/resources/firebase/smeem_fcm.json
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
Expand Down
132 changes: 132 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Smeem (스밈)

> 일기로 시작하는 외국어 훈련, smeem
<img width="786" alt="image" src="https://github.com/Team-Smeme/Smeme-server-renewal/assets/55437339/40136a81-3eba-4f64-bfc2-16e69b214097">

일기로 시작하는 외국어 훈련, 외국어 학습 서비스 Smeem입니다.<br/>
외국어 일기 작성을 통해 일상 속 외국어 출력 훈련을 돕는 학습 서비스입니다.<br/>
언제 어디서든 손쉬운 외국어 훈련을 큰 비용 없이 시작할 수 있습니다.<br/>

<br/>

<img width="778" alt="image" src="https://github.com/Team-Smeme/Smeme-server-renewal/assets/55437339/19cf25ab-c750-4842-b790-7cc3b89dd664">

- 📮 서비스 소개 | [서비스 소개 바로가기](https://linktr.ee/smeem)
- 📱 iOS 다운로드 | [App Stroe 바로가기](https://apps.apple.com/kr/app/smeem-%EC%8A%A4%EB%B0%88-%EC%98%81%EC%9E%91-%EC%98%81%EC%96%B4%EC%9D%BC%EA%B8%B0-%EC%98%81%EC%96%B4%EB%85%B8%ED%8A%B8/id6450711685)

<br/>

## 🧑‍💻 팀원 소개 (Team)

| [김소현](https://github.com/thguss) | [최윤한](https://github.com/unanchoi) |
|:---:|:---:|
|<img width="250" alt="image" src="https://avatars.githubusercontent.com/u/55437339?v=4" />|<img width="250" alt="image" src="https://github.com/Team-Smeme/Smeme-server-renewal/assets/55437339/e546c4ee-b991-4168-bd94-f97d296b095e" />|
| Server Lead Developer | Server Developer |
|- 프로젝트 구조 설계/리팩토링<br/>- 일기 관련 기능 구현|프로젝트 구조 설계/리팩토링<br/> - 인증, 회원 관련 기능 구현|
|[[아티클] FCM으로 푸시알림 기능 구현하기](https://soso-hyeon.tistory.com/87) <br/> [[아티클] 멀티모듈 구조 설계 기록](https://soso-hyeon.tistory.com/83)|[[아티클] Presentation Layer <-> Application Layer DTO 리팩토링](https://ntnb.tistory.com/35)|

<br/>

## ⚒️ 기술 스택 (Tech)
- Java, Spring Boot
- PostgreSQL, Spring Data JPA, QueryDSL
- AWS : EC2, RDS, Code Deploy, Nginx
- JUnit5
- Swagger
- Sentry, Discord WebHook

<br/>

## 🌳 인프라 아키텍처 (Infra Architecture)

<img width="600" alt="image" src="https://github.com/Team-Smeme/Smeme-server-renewal/assets/81692211/ba9bcd6d-0c33-4790-90c1-443b3b410baa" />

<br/>

## 🗂️ 프로젝트 구조 (Project Architecture) : 멀티모듈 구조 (Multi-Module)
Smeem 프로젝트는 단일 모듈 구조에서 시작하여, 코드 간의 의존도와 결합도를 줄여보고자 멀티모듈 구조의 프로젝트로 리팩토링을 진행했습니다.

<img width="600" alt="image" src="https://github.com/Team-Smeme/Smeme-server-renewal/assets/81692211/3dc1d246-bf8f-47f9-bfc6-b22158f6c26f" />

### smeem-api
- 클라이언트와 가장 직접적으로 소통하는 계층으로, API와 비즈니스 로직을 모아둔 모듈입니다.
- Controller, Service 파일을 포함합니다.

### smeem-batch
- batch 작업을 하는 코드를 모아둔 모듈입니다.
- scheduler 파일을 포함합니다.

### smeem-common
- 프로젝트 전반에 공통으로 사용하는 객체를 모아둔 모듈입니다.
- 멀티 모듈 프로젝트 구조의 의미를 극대화하기 위해 최소한의 코드만 포함했습니다.

### smeem-domain
- Domain(Model)과 Database를 호출하는 기능의 코드를 모아둔 모듈입니다.
- Entity, Repository 파일을 포함합니다.

### smeem-external
- Database를 제외한 외부 서비스를 호출하는 기능을 하는 모듈입니다.
- 소셜로그인(카카오, 애플), FCM, Discord Webhook의 외부 API를 호출합니다.

<br/>

## 📌 코드 컨벤션 (Code Convention)

### Code
- 하나의 메서드(method) 길이 12줄, 깊이(depth) 3 이내로 작성합니다.
- Lombok의 val을 사용합니다.

### Entity
- id 자동 생성 전략은 IDENTITY를 사용합니다.
- @NoArgsConstructor 사용 시 access를 PROTECTED로 제한합니다.

### Enum
- Enum 값은 import static 호출로 사용합니다.

### DTO
- Controller에서 요청/응답하는 DTO와 Service에서 사용하는 DTO를 분리합니다.
- Layered Architecture를 엄격하게 준수합니다.
- 확장/번경에 용이하게 합니다.
- 네이밍은 아래와 같이 정의합니다.
- Controller DTO: `${Entity명}${복수형일 경우 List 추가}${행위 또는 상태}${Request/Response}`
- Service DTO: `${Entity명}${복수형일 경우 List 추가}${행위 또는 상태}Service${Request/Response}`

### Response
- 요청 성공 시, BaseResponse<T>와 SuccessCode(인터페이스)의 구현체를 사용합니다.
- 예외 발생 시, Exception과 FailureCode(인터페이스)의 구현체를 사용합니다.

### Service, Repository
- DB를 호출하는 경우 메서드명에 save, find, update, delete 용어를 사용합니다.
- 비즈니스 로직일 경우 메서드명에 create, get, update, delete, 그 외 용어를 사용합니다.
- 복수형은 ${Entity명}s로 표현합니다.
- Service 파일이 비즈니스 로직 5개 이상으로 커지면 조회, 비조회(Transactional)로 클래스를 분리합니다.

<br/>

## 📌 커밋 컨벤션 (Commit Convention)

> **[태그] 제목의 형태**
| 태그 이름 | 설명 |
| --- | --- |
| FEAT | 새로운 기능을 추가할 경우 |
| FIX | 버그를 고친 경우 |
| CHORE | 짜잘한 수정 |
| DOCS | 문서 수정 |
| INIT | 초기 설정 |
| TEST | 테스트 코드, 리펙토링 테스트 코드 추가 |
| RENAME | 파일 혹은 폴더명을 수정하거나 옮기는 작업인 경우 |
| STYLE | 코드 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우 |
| REFACTOR | 코드 리팩토링 |

<br/>

## 🎋 브랜치 전략 (Branch Strategy)

<img width="500" alt="image" src="https://github.com/Team-Smeme/Smeme-server-renewal/assets/55437339/1699ec13-babc-48f7-a914-d5f1fc1d51dd" />

- **main** : 실제 서버(Production)에 출시하는 브랜치
- **develop** : 개발이 완료된 최신 브랜치, 개발 서버(Develop)에 배포
- **feature** : 각 기능을 개발하는 브랜치, 기능 개발 단위로 브랜치 생성, ${이름_#이슈번호}
- **hotfix** : 배포된 버전에서 발생한 버그를 수정하는 브랜치
4 changes: 4 additions & 0 deletions smeem-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ dependencies {
implementation project(':smeem-common')
implementation project(':smeem-domain')
implementation project(':smeem-external')
implementation project(':smeem-batch')

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
Expand All @@ -16,6 +17,9 @@ dependencies {
// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'

// Sentry
implementation 'io.sentry:sentry-spring-boot-starter-jakarta:7.6.0'

}

ext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@ComponentScan(basePackages = {"com.smeem.common", "com.smeem.api", "com.smeem.domain", "com.smeem.external"})
@ComponentScan(basePackages = {"com.smeem.common", "com.smeem.api", "com.smeem.domain", "com.smeem.external", "com.smeem.batch"})
@EnableJpaRepositories(basePackages = {"com.smeem.domain"})
@EntityScan(basePackages = {"com.smeem.domain"})
public class SmemeServerRenewalApplication {
Expand Down
4 changes: 2 additions & 2 deletions smeem-api/src/main/java/com/smeem/api/auth/api/AuthApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import com.smeem.api.auth.api.dto.request.SignInRequest;
import com.smeem.api.auth.api.dto.response.SignInResponse;
import com.smeem.api.auth.api.dto.response.token.TokenResponse;
import com.smeem.api.common.dto.FailureResponse;
import com.smeem.api.common.dto.SuccessResponse;
import com.smeem.api.common.FailureResponse;
import com.smeem.api.common.SuccessResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
Expand Down
23 changes: 13 additions & 10 deletions smeem-api/src/main/java/com/smeem/api/auth/api/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import com.smeem.api.auth.service.AuthService;
import com.smeem.api.auth.service.TokenService;
import com.smeem.api.auth.service.dto.request.SignInServiceRequest;
import com.smeem.api.common.ApiResponseUtil;
import com.smeem.api.common.dto.SuccessResponse;
import com.smeem.common.util.Util;
import com.smeem.api.support.ApiResponseGenerator;
import com.smeem.api.common.SuccessResponse;
import com.smeem.api.support.PrincipalConverter;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.http.ResponseEntity;
Expand All @@ -34,27 +34,30 @@ public class AuthController implements AuthApi {
public ResponseEntity<SuccessResponse<SignInResponse>> signIn(@RequestHeader("Authorization") final String socialAccessToken,
@RequestBody SignInRequest request) throws NoSuchAlgorithmException, InvalidKeySpecException {
val response = SignInResponse.from(authService.signIn(socialAccessToken, SignInServiceRequest.of(request)));
return ApiResponseUtil.success(SUCCESS_SIGNIN, response);
return ApiResponseGenerator.success(SUCCESS_SIGNIN, response);
}

@Override
@PostMapping("/token")
public ResponseEntity<SuccessResponse<TokenResponse>> reissueToken(Principal principal) {
val response = TokenResponse.from(tokenService.issueToken(Util.getMemberId(principal)));
return ApiResponseUtil.success(SUCCESS_ISSUE_TOKEN, response);
val memberId = PrincipalConverter.getMemberId(principal);
val response = TokenResponse.from(tokenService.issueToken(memberId));
return ApiResponseGenerator.success(SUCCESS_ISSUE_TOKEN, response);
}

@Override
@PostMapping("/sign-out")
public ResponseEntity<SuccessResponse<?>> signOut(Principal principal) {
authService.signOut(Util.getMemberId(principal));
return ApiResponseUtil.success(SUCCESS_SIGNOUT);
val memberId = PrincipalConverter.getMemberId(principal);
authService.signOut(memberId);
return ApiResponseGenerator.success(SUCCESS_SIGNOUT);
}

@Override
@DeleteMapping
public ResponseEntity<SuccessResponse<?>> withDrawl(Principal principal) {
authService.withdraw(Util.getMemberId(principal));
return ApiResponseUtil.success(SUCCESS_WITHDRAW);
val memberId = PrincipalConverter.getMemberId(principal);
authService.withdraw(memberId);
return ApiResponseGenerator.success(SUCCESS_WITHDRAW);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.smeem.api.auth.jwt;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.smeem.api.common.ApiResponseUtil;
import com.smeem.api.common.dto.FailureResponse;
import com.smeem.api.support.ApiResponseGenerator;
import com.smeem.api.common.FailureResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -33,6 +33,6 @@ private void setResponse(HttpServletResponse response) throws IOException {
}

private ResponseEntity<FailureResponse> getFailureResponse() {
return ApiResponseUtil.failure(INVALID_TOKEN);
return ApiResponseGenerator.failure(INVALID_TOKEN);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.smeem.api.auth.jwt;

import com.smeem.common.exception.AuthException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import lombok.val;
import org.springframework.security.core.context.SecurityContextHolder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import com.smeem.api.diary.service.DiaryCommandService;
import com.smeem.api.member.service.MemberBadgeService;
import com.smeem.api.member.service.TrainingTimeService;
import com.smeem.common.exception.TokenException;
import com.smeem.domain.member.adapter.member.MemberDeleter;
import com.smeem.domain.member.adapter.member.MemberFinder;
import com.smeem.domain.member.adapter.member.MemberSaver;
import com.smeem.external.oauth.exception.TokenException;
import com.smeem.domain.member.model.Member;
import com.smeem.domain.member.model.SocialType;
import com.smeem.external.oauth.apple.AppleService;
Expand Down
4 changes: 2 additions & 2 deletions smeem-api/src/main/java/com/smeem/api/badge/api/BadgeApi.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.smeem.api.badge.api;

import com.smeem.api.badge.api.dto.response.BadgeListResponse;
import com.smeem.api.common.dto.FailureResponse;
import com.smeem.api.common.dto.SuccessResponse;
import com.smeem.api.common.FailureResponse;
import com.smeem.api.common.SuccessResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.smeem.api.badge.api.dto.response.BadgeListResponse;
import com.smeem.api.badge.service.BadgeService;
import com.smeem.api.common.ApiResponseUtil;
import com.smeem.api.common.dto.SuccessResponse;
import com.smeem.common.util.Util;
import com.smeem.api.support.ApiResponseGenerator;
import com.smeem.api.common.SuccessResponse;
import com.smeem.api.support.PrincipalConverter;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.http.ResponseEntity;
Expand All @@ -26,8 +26,9 @@ public class BadgeController implements BadgeApi {
@Override
@GetMapping
public ResponseEntity<SuccessResponse<BadgeListResponse>> getBadges(Principal principal) {
val response = BadgeListResponse.from(badgeService.getBadges(Util.getMemberId(principal)));
return ApiResponseUtil.success(SUCCESS_GET_BADGES, response);
val memberId = PrincipalConverter.getMemberId(principal);
val response = BadgeListResponse.from(badgeService.getBadges(memberId));
return ApiResponseGenerator.success(SUCCESS_GET_BADGES, response);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.smeem.api.common.dto;
package com.smeem.api.common;

import lombok.Builder;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.smeem.api.common.dto;
package com.smeem.api.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
Expand Down
4 changes: 2 additions & 2 deletions smeem-api/src/main/java/com/smeem/api/diary/api/DiaryApi.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.smeem.api.diary.api;

import com.smeem.api.common.dto.FailureResponse;
import com.smeem.api.common.dto.SuccessResponse;
import com.smeem.api.common.FailureResponse;
import com.smeem.api.common.SuccessResponse;
import com.smeem.api.diary.api.dto.request.DiaryCreateRequest;
import com.smeem.api.diary.api.dto.request.DiaryModifyRequest;
import com.smeem.api.diary.api.dto.response.DiaryCreateResponse;
Expand Down
Loading

0 comments on commit 1dc8c85

Please sign in to comment.