Song[coding diary index]

Song 배열에 코딩 흔적 남겨두기

프로젝트 회고록

[PE 2] 매일코테(Mail-cote): Go언어 프로젝트 해보기

singsangssong 2025. 1. 2. 20:01
반응형

기말고사 2주전에 마무리했던 프로젝트인데, 시험기간때문에 방학이 되어서야 회고록을 쓰게 되었다...

그럼 출발~

시작계기

GDG(Google Developer Group)동아리의 서버 파트에서 백엔드 사이드 프로젝트를 참여했던 프로젝트이다.

최신기술 중 몇가지를 적용한 프로젝트(grpc, OAuth, MSA, Kubernetes 등..)를 만드는 것이 목적이었고, 간단한 서버프로그램을 만드는 것이 동아리의 취지였다. (취지는 매우 간단했다. 그냥 '최신기술 써보고 익숙해지자!' 느낌?)

나도 '최신기술 하나 이용해보고, 익숙해지자!' 라는 가벼운 취지였지만, 결국 깊은 공부를 하게 된 프로젝트가 되었다.

 

하지만... 2명이 팀이 되어 하는 프로젝트에서 열정적인 팀원덕분에 프로젝트의 규모가 커졋고, 덕분에 기말고사를 공부할 시간을 줄여가며 프로젝트를 했던 뜻깊은(?) 경험이 될줄이야...

 

프로젝트 소개

프로젝트 계기

Mail-Cote라는 프로젝트는 매일메일(Maeil-mail)에서 영감을 받아온 프로젝트이다. 

 

먼저 매일메일은 CS공부를 한번에 몰아서 하기보다 매일 하나씩 CS개념을 공부하는 것이 목적이다. 이에 email을 통해서 오전 7시에 사용자의 분야에 맞는 문제가 전송되었고, 아래와 같은 방식으로 메일을 받고 공부할 수 있다.

백엔드 파트 예시 질문

💡메일마다 코딩테스트 문제를 메일로 보내주는건 어떨까?💡

 

이렇게 탄생한 것이 바로 매일코테(Mail-Cote)이다.

 

프로젝트 기능 소개

위 매일메일의 CS문제 -> 알고리즘 문제로 바뀌었다고 생각하면 된다.

문제들은 백준에서 데이터를 (1)크롤링한 후, 각 (2)문제 정보를 .json파일로 만들어, 이를 google cloud bucket에 저장했다.

( 폴더구조: /problems/난이도/상세 -> ex: /problems/bronze/5 )

 

사용자는 그저 streamlit을 통해 만든 프론트에서 회원가입만 수행하면 끝이다!

간단하게 만든 화면 UI

 

매일 오전 7시마다 메일이 전송되도록 수행했고, 메일을 받으면 아래와 같은 모습이다.

프로젝트 진행기간

2024.10.29. ~ 2024.12. 5. 

10/29 팀 배정 이후, 11/9부터 본격적인 회의를 시작했다. 

프로젝트 기술 스택

  • Language: Python, Go
  • Framework: gRPC
  • Infrastructure: GKE, Docker, GCS, Cloud sql
  • Plus: Nginx, Streamlit

아키텍쳐 소개

1️⃣ gRPC를 통한 모듈 간 통신

  • gRPC: 원격 모듈의 함수를 로컬처럼 호출할 수 있는 원격 프로시저 프로토콜.
  • 모듈 간 DB 연결 최소화 및 처리 속도 최적화.

2️⃣ GKE를 통한 Pod 관리

  • GKE: Kubernetes 기반의 컨테이너 오케스트레이션 플랫폼.
  • Dockerfile, ReplicaSet 등을 활용하여 서비스 안정성 확보.

3️⃣ GCS Bucket을 이용한 데이터 관리

  • Google Cloud Storage: 크롤링 데이터를 .json 파일로 저장 및 관리.
  • 외부 서버에서 디지털 데이터를 효율적으로 관리.

4️⃣ Cloud sqld을 통한 RDBMS 관리

  • 자동 백업, 및 손쉬운 복구
  • Google Cloud 관리형 서비스이므로, 호율적 운영 및 확장 가능성 측면에서 좋음.

필자는 1️⃣, 4️⃣번을 맡아 수행했다.

 

잠깐 grpc 분석하기!

grpc의 최대장점은 다른 모듈에 내장된 함수를 쉽게 사용할 수 있다는 점이다.

(현재 모듈에서 원하는 함수가 다른 언어로 짜여진 모듈에 내장된 함수로 존재한다면, 그 모듈의 함수를 grpc로 이용 + 다른 모듈에서 최고 성능으로 수행가능. 언어 독립적임.)

 

아래 플로우를 grpc로 만들어 각각 모듈에서 최적의 성능으로 작업을 수행할 수 있도록 했다.

메일이 전송되는 플로우 (= mail pod 동작과정)
1. 가입된 사용자정보 user db에서 불러오기 
2. 사용자의 난이도에 맞는 문제정보 랜덤으로 불러오기
ㄴ 이때, 사용자에게 보냈던 문제정보를 가져와 중복된 문제전송을 막음.(history db접근)
3. 랜덤으로 불러온 문제 SMTP로 이메일 전송
4. 보냈던 문제 정보 DB에 저장하기

 

여기서 2번은 각각 member, history 2개의 모듈에 접근해서 동작을 수행하도록 했다. 

 

중요 문제해결과정

🚨이슈🚨

사용자가 매우 많다면, 7시부터 user테이블에 저장된 사용자를 모두 불러오기를 기다리는 것은 비효율적이라 생각했다.

7시에 mail pod -> member pod 사용자 전체 조회를 수행하면, 10만명의 정보를 불러오기까지 mail pod는 대기해야한다.

 

🍀해결🍀

그렇기에 메일 서버에서 맴버 서버에 사용자 조회를 요청하면, (mail pod -> member pod)

일정 인원만(예시로 1000명) mail pod에게 전송한 후, (member pod -> mail pod)

mail pod는 사용자에 맞는 문제를 선정하고, (mail pod <-> gcp bucker)

member pod는 다시 일정 인원을 조회하는 방식으로 문제를 해결했다. (member pod -> mail pod)

 

이렇게 하면 mail pod가 대기하는 시간을 최소한으로 줄이며 문제를 해결할 수 있었다!

 

 

grpc 파일 작성 방법!

1. .proto파일을 작성

2. 명령어를 통해서 .pb.go파일 생성함.

3. 생성한 함수를 작성하는 서버 실행.

// mail.proto파일
// 서비스 정의는 프로토콜 버퍼의 정의로 시작. 보통 proto3
syntax = "proto3";

// 사용하는 페키지 이름
package mail;

option go_package = "github.com/mail-cote/go-server/mail-service/mail";

// gRPC 서비스의 서비스 인터페이스를 정의
service Mail {
    // 1. mysql 연결 후, 유저 정보 가져오기
    // 2. 버킷에서 랜덤 파일 가져오기
    // 3. smtp로 메일 전송하기
    rpc FetchQuizFromBucket (FetchQuizFromBucketRequest) returns (FetchQuizFromBucketResponse);
    rpc SendMail (SendMailRequest) returns (SendMailResponse);
}

// 정의한 함수의 메세지 형식 및 타입 정의
message FetchQuizFromBucketRequest {
    string level = 1;
}

message FetchQuizFromBucketResponse {
    int64 quizId = 1;
    string quizContent = 2;
    string message = 3;
}

message SendMailRequest {
    string sendTo = 1;
    string sendFrom = 2;
    string quizContent = 3;
}

message SendMailResponse {
    string message = 1;
}

명령어 실행하면, 2개의 grpc파일 생성되는 것을 확인.

 

분리된 서버 모듈간의 통신을 grpc로 맡았고, 각각 도미엔단위로 모듈을 나누어 처리하도록 했다. 

이번 프로젝트의 규모 단위가 작은 관계로 grpc를 활용해 msa로 분리한 것과 monolithic의 큰 차이가 없지만 모듈간 통신에 좋은 기술임을 확인함.

 

프로젝트 후기 및 배운 점

자가 피드백

잘한 점

grpc에 대해서 직접 사용하고, 모듈간의 통신이라는 일련의 과정을 직접 코딩하고 눈으로 확인했다는 점이 뿌듯하다. 서버와 서버간의 통신과정을 터미널창을 통해 확인하고, 메일이 전송되는 과정을 모두 개발하고 나니 grpc가 동작하는 원리를 이론이 아니라 실습에 적용할 수 있다는 자신감도 생길 수 있는 경험이었다.

 

또 매번 http를 이용한 API서버, 특히 MVC구조에 국한된 다소 정적인 코드를 짜고 있었다고 생각했다.(물론 매우 많이 부족한 지식으로 짰던 서버지만, 단조로운 느낌을 늘 받았다... dto, entitty, controller, service...) 

그래서일까 이번에 디자인 패턴에 국한하지 않고, 순수하게 내가 원하는 프로젝트를 하다보니 시간은 오래걸렸지만 뭔가 하고싶은 공부를 위해서 공부했던 점이 가장 좋았고, 재미있던 프로젝트였다.

부족했던 점

기록하는 습관을 가져야함을 알았다. 문제해결과정에서 막혔던 부분이 정말 많았는데, 이를 기록해두지 않으니 막상 회고할때 어디가 어떻게 막혔는지 기억이 나지를 않는다. 한달이면 기억하겠다는 안일한 생각이 아쉽다. 문제 해결과정에서 학습했던 기억을 많이 잃게 된 점이 가장 아쉽다.

향후 계획 및 마무리

먼저 서비스적 측면으로는 수정할 부분이 매우 많다고 생각한다.

1. 1일1알고리즘을 위해서는 백준 문제보다는, 구현을 제외한 알고리즘만 생각할 수 있는 문제 사이트가 좋을 것 같다.

2. 문제를 보내주는 메일 html 파일의 UI/UX문제를 고려하면 좋을 것 같다.

3. Oauth 연동하기

 

go언어는 현재 2번째 언어로 사용할 생각이었기에, 위 일련의 과정을 spring/java 형태로 변형하고 싶다. 그렇다면 spring 코드 생선성과 실력이 많이 늘지 않을까 싶다.

 

 

GitHub - mail-cote/go-server: 📢 매일코테(Mail-Cote) 백엔드 레포

📢 매일코테(Mail-Cote) 백엔드 레포. Contribute to mail-cote/go-server development by creating an account on GitHub.

github.com