Giter Club home page Giter Club logo

mailservice's Introduction

mailService

서비스 구현 목적 & 기능

Spark 서비스와 같은 통합 메일 서비스에 필요한 비동기 처리를 통한 예약 전송 등을 공부 목적으로 구현.

진행기간: 2022-07-29 ~ 2022-10-27

기능

  • 메일 서비스의 정보(SMTP)를 저장하고, 민감정보는 암호화 적용
  • 즉시전송과 예약전송을 통해 유저가 필요한 시점에서 메일을 전송할 수 있게 관리
  • 수신 기능은 구현하지 않음

구조

데이터 구조

  • 사용자와 메일 서비스에 필요한 인증정보는 relation 으로 처리.
  • 메일 데이터는 수신자, 송신자가 같은 서비스의 유저일 경우, 2개의 중복데이터를 방지하고자 relation 없이 수신자ID, 송신자ID 를 정의하여 쿼리를 통해 조회할 수 있도록 의도.
  • BaseEntity 를 통해 생성시간, 수정시간 같은 공통 필드 정의

서비스 처리 흐름


asynchronous & multi-module

즉시전송과 예약 전송 기능을 고려하여 비동기적으로 설계하였다. 서버에서 즉시전송과 예약 전송을 동시 처리가 필요하고, 사용자의 요청시간을 줄이기 위해 적용하였다. 또한 메일 서비스 특징 상, 메일 요청 완료까지 대기할 필요가 없고, 성공/실패 여부에 따라 메일함 분류가 나뉠 수 있기 때문.

  • 사용자는 메일 전송 요청시 즉시전송/예약전송 여부에 따라 전송내역생성/즉시전송메시지큐전송 으로 나뉨
  • 즉시전송 Consumer 는 메시지큐로부터 메일을 전달받아 즉시 전송을 처리하고, 전송내역을 DB에 생성한다.
  • 예약전송큐삽입 Consumer 는 1분마다 Task 가 실행되어 해당 시간에 전송되어야할 메일을 예약전송메시지큐에 삽입한다.
  • 예약전송 Consumer 는 예약전송큐로부터 전달받은 메일을 전송하고, 전송내역을 DB에 업데이트한다.

즉시전송/예약전송 모두 단일 메일전송을 성공시키기 위해 Kafka 메시지큐를 도입했다.

multi-module 코드 구조

mail-core: 메일 Entity, Repository 와 같은 공통 모듈 정의

mail-api: 메일 전송 요청을 받기 위한 모듈로 메시지 produce.

mail-service: 실제 메일 전송을 담당. 즉시전송/예약전송을 처리하고 메시지 consume.


간단 작업내용

  • 인증은 JWT 로 구현.
  • HTTP 상태 관리는 org.springframework.http.HttpStatus 를 활용
  • Controller-Service-Repository layer를 기준으로 로직마다 전달할 데이터는 모두 DTO로 관리
  • 내부 로직에 필요한 테스트케이스 정의

피드백과 self-질문

피드백

  • 모놀리식으로도 구현가능한 구조이기에 multi-module 을 공부 목적 외로 필수로 도입할 필요가 없다.

  • 구현과 문서 명시 작업은 동시에 처리하는게 아니다. 문서를 먼저 명시하고 작업할 수 있게 충분히 생각할 시간과 과정이 필요.

  • try-catch는 가능하면 controller 이전 다른 계층에서 처리하는게 좋다.

  • try-catch 단위: validation() -> logic() -> return

  • 코드 depth는 최소화하자. 가급적 로직에서 2-depth를 넘어가면 가독성이 떨어지고, 코드가 복잡해짐.

  • Optional 사용 시 여러 계층까지 데이터가 오가기 때문에, 이 과정에서 검증하는 로직이 필요하다. 코드량이 늘어나 코드 커버리지가 낮아질 수 있다.

  • 모든 기능은 명칭과 처리가 동일시하도록 정의하자.

  • admin 기능과 user 기능은 분리하여 정의하자. (백오피스 기능 구현 중 피드)

  • 리소스와 서비스로직은 별도의 controller, service로 혼용되지 않도록 관리하는게 좋다.

  • RESTful 한 설계 시 상위-하위 리소스에 맞게 URI를 명시하자.

  • 특정 값을 파싱/입력 시 올바른 처리결과 유무를 위해 handling 및 테스트케이스 필요. (더 많은 unit-test..)

  • DTO-entity 간 from/to 전략을 적절히 사용하자.

  • 각 entity, DTO 의 유효성 기준을 미리 적용하자.

  • null 을 확인하는 과정은 오류 혹은 동작정지를 일으키는 주범이다. default 실패 값을 설정하거나, 꼭 null 확인이 필요한 경우는 Objects.isNull을 사용하자.

  • 반복 사용되지 않은 변수는 선언 없이 호출로만 처리해도 무관. 단, 명칭은 명확해야함.

  • Spring은 기본적으로 singleton 패턴을 사용하기 때문에, utility 기능은 꼭 정적 클래스로 선언하지 않아도 된다. @Component 와 같이 Bean 으로 등록하여 사용해도 큰 성능차이를 보이지 않는다.

  • 테스트케이스는 모든 기능에 대해 SUCCESS/FAIL 케이스를 정의하는게 좋다. (TDD는 어렵다..)

  • 하나의 테스트는 1개의 요소를 처리한다. 여러개의 테스트를 하나의 테스트에서 진행하지 말자.

self-질문

  • 비동기 프로세스로 메일 전송을 보낸다고 가정했을 때, 서비스가 감당할 수 있는 트래픽의 규모를 기준으로 테스트는 어떻게 진행할 수 있을까? -> 동시성 테스트는 다중 스레드를 통해 시도해볼 수 있을 것 같다. 대규모 트래픽 기준의 테스트 영역은 더 공부가 필요하다.
  • PR의 크기가 큰 경우가 많다. 협업 시 큰 문제가 될 텐데 어떻게 줄일 수 있을 것인가? -> 기능 구현에 필요한 스펙문서를 정확하게 정의한다. 그리고, 각 기능에 필요한 요소를 미리 정의하고, 다이어그램과 같은 툴을 이용해 코드 구조를 미리 정하는 것도 좋은 방법이다.

mailservice's People

Contributors

wiggleji avatar

Stargazers

 avatar

Watchers

 avatar  avatar

mailservice's Issues

[Util] JWT 인증 기능 for User

Issue #12
의 유저 로그인과 모든 기능의 인증/인가를 위한
JWT 발급/검증 기능

  1. JWT provider 생성
    • 토큰 발급
    • 토큰 정보로 유저 정보 조회
    • 토큰 검증
  2. SpringBoot application에 전체 JWT 인증/인가 프로세스 적용

상세스펙

  • TokenProvider
    • generateToken
    • getUserIdFromToken
    • validateToken
  • TokenAuthenticationFilter
    • spring OncePerRequestFilter 상속
    • doFilterInternal (override)

JWT는 jjwt 사용

[feature] 도메인 설계 수정

image

기존 도메인 설계 #1 에서 문제점

  1. 타도메인으로 메일 전송 시 MailMetadata-User relation 관계 성립 불가
  2. 메일 조회 시 서로 다른 테이블 조회 (송신-Mail/수신-MailMetadata)

변경점

image

  1. MailMetadata 와 User relation 삭제
  2. UserMail에 folder_id를 추가하여 수신/발신 여부 분리
    • 같은 메일 서버의 계정에 메일을 보내면 메일 내역은 1개만 생성. UserMail에 각 수신/발신에 따라 folder만 지정.
      중복데이터 생성 없이 관리 가능 (단점: 쿼리 join & cc/bcc 로직가 별도로 필요)
  3. Mail의 sender_id(전송자)를 user_id(메일내역 소유자) 와 email_from(발신자) email_to(수신자) 로 대체
    • User-Mail 간 ManyToMany 관계로 같은 도메인의 유저에게 전송 시 user-mail 내역 생성 (Mail/MailMetadata 중복데이터 생성 방지)
    • 수신/발신 여부 분리를 하였기 때문에, email_from/email_to 는 서로 다른 메일이 되어도 상관없음
  4. cc/bcc/to 는 기존과 동일. Mail의 to는 최초 답장자. 그 외는 추가 (sort 기준 없음)

[domain] 메일 controller 배치

EmailController - 메일 정보(삭제는 추후 soft delete 적용 후 테스트케이스 작성) + 메일 전송 요청(EmailSend)
EmailMetadataController - 메일 전송정보


기타 작업

  • 각 controller 별 테스트케이스 작성
  • 모든 기능 검수 & 필요시 수정

[feature] 메일 전송 서비스

메일 전송에 필요한 데이터 & Java Mail API 호출을 위한 서비스 개발

  • 메일 메타데이터(유저메일 / SMTP 정보)
  • 메일 메타데이터 생성 및 관리 서비스
  • 메일 전송 서비스 (메일 메타데이터 & 유저 유효성 검사 후 메일 저장)

유의점

  • 기능 및 테스트케이스 작성 시, Java Mail API 호출 부분은 mocking 처리할 수 있도록 설계해야함

[feature] 이메일 서비스 DTO & DAO

  1. Entity & Table 구성 -> #2 완료

image

  1. DTO
  • User

    • 유저 정보 : username, email [id,password]
    • 로그인 : email, password
    • 회원가입 : username, email, password
  • Mail

    • 메일 목록 조회_메일 목록 정보
      id, subject, content, dateTimeCreated, dateTimeSend, sender [thread_id, metadata]
    • 메일 상세 조회
      id, subject, content, dateTimeCreated, dateTimeSend, sender, thread_id, metadata
    • 메일 작성_메일 본문
      subject, content, sender, metadata(MailMetadata DTO로 제공)
  • MailMetadata

    • 메일 목록 조회_수신자 정보
      id, mail, receiver
    • 메일 상세 조회_정보 목록
      id, carbonCopy, receiver
    • 메일 작성_메일 수신자정보
      carbonCopy, receiver
  • MailFile

    • 메일 상세 조회_첨부파일 목록
      id, fileType, fileUrl
  • UserSignature

    • 메일 작성_유저 서명 목록 조회
      id, signature
  1. DAO (JPA사용)
  • UserRepository
  • MailRepository
  • MailMetadataRepository
  • MailFileRepository
  • UserSignatureRepository

  1. Service

  2. Controller

[domain] 메일 서비스 Entity 설계

목적

일대일 관계로 메일을 주고 받기 위한 도메인 설계

필요한 정보

  • 메일소유자 ID
  • 발신자 메일주소
  • 수신자 메일주소
  • 메일 제목
  • 메일 본문
  • 발송시간
  • 수신시간

ERD

image

간단한 설계 설명

  • 데이터는 위에 명시한 정보에 맞게 테이블 설계
  • user-email 간 relation을 정의하지 않은 이유는 메일을 전송하는 과정에서 유저 정보의 조회 외는 필요없기 때문에 동일한 트랜젝션에서 정의할 필요가 없어보여 초기설계에서 제외.
    우아한테크세미나 참고

[DOCUMENT] 개발 프로세스 재정의

Version History


개발 프로세스

Git & Versioning convention

1. Git

git-flow 로 형상관리

  • main: 기능이 성공적으로 반영되어 정상 작동하는 코드 (release 적용)

  • release: develop에 feature가 성공적으로 반영된 상태로 정상 작동할 수 있을 때 반영

  • develop: 개발 기능이 적용된 코드

  • feature: 새로운 기능 코드

2. Versioning

x.x.x 버전으로 진행

0.0.x : 새로운 기능 추가 / 안정화, 리팩토링 작업
- 홀수: 기능추가
- 짝수: 안정화, 리팩토링 작업
-> github label로 정리&관리

0.x.0 : 실제 release (develop -> main)

x.0.0 : 기간에 따른 안정적 release (해당 프로젝트에서는 기간이 명시되어 있지 않아 적용하지 않음)


도메인 설계

각 version 별로 필요한 도메인 ERD와
새로운 기능, 작업은 가능한 점진적으로 추가하며 issue로 명시

merge conflict & github actions CI 적용

443ad0d
1615781
9c05ea0

-> github actions CI 적용

  • JDK & mysql 설정
  • maven cache 적용
  • maven verify
  • 시간 출력

merge conflict

  • github actions 적용 과정에서 기존 domain, controller, service 를 도메인별로 분리하면서 제거가 되지 않아, 에러 발생
    -> package path 수정으로 해결

DTO & DAO 추가

DB ERD

image

DTO

  • UserDto

    • 유저 정보 (UserInfo)
      username, email [id,password]
    • 로그인 (UserLogin)
      email, password
    • 회원가입 (UserSignUp)
      username, email, password
  • MailDto

    • 메일 목록 조회_메일 목록 정보 (MailInfoList)
      id, email_from, email_to, subject, content, send_time, receive_time, mail_file
    • 메일 상세 조회 (MailInfo)
      id, subject, content, send_time, sender, thread_id[-> thread_id 가진 threadMails 조회], metadata
      threadMails - thread_id를 가지며 user_mail 내역이 있는 Mail 목록 (MailList)
    • 메일 작성_메일 본문 (MailCreate)
      email_from, email_to, subject, content, send_time, thread_id, emailToList, emailCcList, emailBccList
  • UserMailDto

    • 메일 목록 조회_유저/메일함 메일 조회 + 동일 도메인 메일 전송시 내역 생성 (UserMailInfo)
      user_id, mail_id, folder_id
  • MailMetadataDto

    • 메일 목록/상세 조회_수신자 정보 (MailMetadataInfo)
      id, name, email, carbon_copy_type
    • 메일 작성_수신자정보 생성 (emailCcList, emailBccList -> MailMetadata 변환시) (MailMetadataCreate)
      mail_id, name, email, carbon_copy_type
  • MailFileDto

    • 메일 상세 조회_첨부파일 목록 (MailFileInfo)
      id, fileType, fileUrl
  • UserSignatureDto

    • 메일 작성_유저 서명 목록 조회 (UserSignatureInfo)
      id, signature

DAO

  • UserRepository
  • MailRepository
  • UserMailRepository
  • MailMetadataRepository
  • MailFileRepository
  • UserSignatureRepository

[feature] 메일 메타데이터 민감정보를 위한 암호화 적용

AS-IS: 메일 메타데이터 저장 시 암호 같은 민감정보가 plain-text 로 저장되고 있음

TO-BE: 메일 메타데이터 저장 시 암호화된 값으로 DB에 저장되도록 수정 & 서비스 로직에서 필요한 경우, 복호화된 값으로 로직처리 필요

필요한 기능: 양방향 암호화 모듈 & Entity 저장/조회 시 암호화/복호화 적용

  • 양방향 암호화 모듈: 추가완료

    @Slf4j
    @Component
    public class Encryption {
    @Value("${encryption.secret-key}")
    private String secretKey;
    public static String algorithm = "AES/CBC/PKCS5Padding";
    private final static String initialVector = "mailEncrypt12345";
    public String encryptAES256(String text) {
    try {
    Cipher cipher = Cipher.getInstance(algorithm);
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector.getBytes());
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
    byte[] encrypted = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
    return Base64.getEncoder().encodeToString(encrypted);
    } catch (Exception e) {
    log.error("Error while encrypting value: " + text);
    throw new RuntimeException(e);
    }
    }
    public String decryptAES256(String cipherText) throws Exception {
    try {
    Cipher cipher = Cipher.getInstance(algorithm);
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector.getBytes());
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
    byte[] decodedBytes = Base64.getDecoder().decode(cipherText);
    byte[] decrypted = cipher.doFinal(decodedBytes);
    return new String(decrypted, StandardCharsets.UTF_8);
    } catch (Exception e) {
    log.error("Error while decrypting value: " + cipherText);
    throw new RuntimeException(e);
    }
    }
    }

  • 암호화/복호화 적용: Entity 혹은 Service에서 저장/로드 시 자동으로 암복화가 되도록 암호화 모듈을 적용시켜줘야함

[domain] 메일 도메인 reset & 유저 도메인 생성

User

서비스 사용자를 위한 도메인으로 유저 정보의 저장/조회를 위한
로그인/회원가입 기능을 추가

  1. Entity 설계

image

User

  • id: 서비스의 유저고유 id
    • unique, auto-generate
  • username: 유저명
  • password: 유저 인증을 위한 패스워드
    • encrypted
  • email: 유저 로그인/메일 송수신을 위한 메일
    • unique
  1. 서비스 요구사항
  • 유저 조회
    • id, email 로 유저 정보 조회 -> 인증, 유저 정보 조회에 사용
  • 유저 로그인
    • email/password 로 유저 로그인
  • 유저 회원가입(계정생성)
    • username/email/password 로 새로운 사용자 생성
  1. 상세 설계

UserService: User Entity에 대한 정보 처리

  • loadUserByEmail: 이메일로 유저정보 조회
    • args: (String) email
    • return: (Entity) User
  • loadUserById: User id로 유저정보 조회
    • args: (Long) id
    • return: (Entity) User
  • registerUser: 새로운 유저정보 생성
    • args: (String) username, (String) email, (String) password
    • return: (Entity) User

DTO

  • UserDto: 민감정보 제외
    • username, email
  • UserSignUpDto: 유저 회원가입
    • username, password, email
  • UserLoginDto: 유저 로그인
    • email, password

UserController: User domain과 관련된 controller

  • userSelfDetail: 요청한 유저 본인의 정보 조회
    • args: Null
    • return: (UserDto) user

AuthController: User 생성/인증하는 controller

  • userSignUp: 유저 회원가입
    • args: UserSignUpDto
    • return: (UserDto) user
  • userLogin: 유저 로그인
    • args: UserLoginDto
    • return: (String) jwtToken

[domain] 메일시스템 설계

메일시스템 설계 (Entity & Service)

메일 SMTP 서버 인증과 메일 전송 과정에 필요한 데이터 모델과 로직 처리 서비스 설계

메일 전송 프로세스 정리

  1. ☑회원가입 -> AuthService
  2. ☑로그인 -> AuthService
  3. 메일인증정보 확인
  4. 메일인증정보의 메일 조회
  5. 메일 작성 정보 대입하여 메일 전송

서비스 설계방향

Mail API는 Java Mail API 사용 (https://javaee.github.io/javamail/docs/api/)

MailService - 서비스에 저장된 유저의 인증정보로 메일 전송 서비스 제공

ERD (Entity)

image

  • 기존 메일 정보에 TO, CC, BCC 정보를 저장하는 필드 추가
  • 외부 메일 서버를 거쳐 메일을 전송하기 때문에 SMTP 서버와 인증/연결에 필요한 정보를 저장하는 UserEmailInfo 추가
    • smtp host, port, 인증(username, password), email

DTO

  • 메일 DTO
    • 조회: EmailDto
    • 생성: EmailCreateDto
  • 유저메일정보 DTO
    • 조회: EmailMetadataDto, MailMetadataListDto
    • 생성: MailMetadataCreateDto

공통 제외 목록

  • BaseEntity 정보
  • 조회시 UserId (생성시 필요)

[feature] mailService 스펙

내부/외부 로직으로 분리

  1. internalMailService

    1. 메일 조회: 목록/상세
    2. 메일 생성
      • Mail/MailMetadata/MailFile
  2. externalMailService

    1. 외부 메일로 메일 전송
    2. 외부 메일로부터 메일 받기

[feature] 도메인 설계

image

user

  • username : 인증/인가를 위한 유저명
  • password : 인증/인가를 위한 패스워드
  • email : 이메일

mail

  • sender_id : 송신자 user_id
  • subject : 메일 제목
  • content : 메일 본문
  • datetime_created : 메일 생성시간
  • datetime_send : 메일 전송시간
  • reply_to : 메일 스레드 parent_id

mail_metadata

  • mail_id : 메일 id
  • receiver_id: 수신자 user_id
  • carbon_copy_type : cc/bcc/to [Enum] 참조/숨은참조 여부
  • datetime_crated : 메일 metadata 생성시간

mail_file

  • mail_id : 메일 id
  • file_type : 첨부파일 파일 종류
  • file_url : 파일 URL

user_signature

  • user_id : 유저 id
  • signature : 서명내용

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.