애자일하게 졸프하기; 1학기
프로젝트 시작하기
컴퓨터공학과 학생이라면 누구나 4학년에 프로젝트를 의무적으로 하게된다. 갖은 풍파(?)를 견뎌내고 29살에 4학년을 다니고 있는 나로서는 꽤나 큰 고민거리였다. 졸업 프로젝트를 남들처럼 대충대충 CRUD 어플리케이션만 만들기엔 내 시간이 너무 아까웠다. 그렇다고 어마어마한 시간을 쏟아부어 내가 만족할만한 퀄리티의 소프트웨어를 내기엔 부담이 컸다.
나는 스타트업에서 개발자로 일하고 있으며, 코로나라는 특수한 상황으로 원격 수업을 진행하는 탓에 학교를 마치겠다고 복학했다.
평소 친하게 지내던 공부잘하는 학교 후배와 팀업하기로 했다. 대학원 진학을 결정한 후배와 회사를 다니고 있는 나는 시간대비 효율을 극대화할 방법을 찾았다.
😄희망편 — 개발 원칙 새우기
내가 다니는 회사 “HBsmith”는 100% 리모트워크로 개발팀이 운영되고 있다. 내가 원하는 공간에서 언제든지 일할 수 있는 환경이고, 덕분에 학업을 마칠 수 있는 계획을 세울 수 있었다. 나는 이 회사에서 이전에 경험해보지 못한 매우 좋은 개발문화를 경험하고 있다. 모든 개발자들이 원격으로 일하면서 좋은 효율을 내고있는 신기한 경험을 하는 중이다.
이 회사 블로그 글(https://brunch.co.kr/@hbsmith/19)에서 HBsmith 만의 개발문화를 설명하고 있다. 윤제상 CTO님의 영상 인터뷰도 있으니 관심있는 사람들은 확인해봐도 좋다.
2주 단위의 스프린트를 7회 진행해서 나름 준수한 성과를 냈다고 생각한다. 프로젝트를 시작할 당시엔 이 문화를 졸업프로젝트 팀에 도입한다면 분명히 좋은 효율을 낼 수 있다고 생각했다. 이 덕분에 단지 졸업프로젝트의 의무방어(?) 차원을 넘어서 실서비스 릴리즈까지의 플랜을 세우게 되었다.
이가 없으면 잇몸으로, Jira대신 Youtrack
우리는 각자 해야할 일들이 많았다. 졸업프로젝트에 온 신경을 집중할 수 있는 입장은 아니었다. 그러므로 2주 길이의 스프린트 개념을 도입해 2주간 할 일들을 스토리포인트로 산정하여 관리했다.

위에서도 이야기했지만, 2주의 스프린트를 7번 진행하니 대충 MVP가 나왔다.
문서화, 시간 낭비를 줄여준다.

팀 빌딩하고 프로젝트를 시작하면서 가장 신경쓴 부분이 바로 문서화다. 프로젝트를 시작하면서 이 레포지토리(https://github.com/project-agd/docs)에 문서화를 시작했고, 이는 프로젝트 수업에서 제출해야되는 보고서들을 매우 효율적으로 쳐낼 수 있었다.
API 문서 자동화


우리는 인증, CRUD 등의 메인 백엔드로 Java & spring boot 과 추천 시스템으로 python & FAST API 를 사용하였다. FAST API 프레임워크는 모든 API가 자동으로 문서화되는 기능을 기본 탑재하고 있다. Spring boot 에도 swagger 를 붙여 문서화했다.
aws 아키텍쳐 그래프

다양한 인프라 그래프 툴들이 있지만, 무료로 사용하기엔 제한적이기 때문에 조사하다가 파이썬 라이브러리인 diagrams(https://github.com/mingrammer/diagrams)를 찾았다. 이를 이용해서 위의 그림을 그렸다. 코드는 아래와 같다.
from diagrams import Diagram, Cluster
from diagrams.aws.compute import EKS, ECS, Lambda
from diagrams.aws.database import Aurora, Dynamodb
from diagrams.aws.integration import SQS
from diagrams.aws.ml import Sagemaker
from diagrams.aws.network import CloudFront, VPCRouter, ClientVpn
from diagrams.aws.storage import S3
from diagrams.generic.device import Mobile
from diagrams.generic.os import Ubuntu
if __name__ == "__main__":
graph_attrs = {
"pad": "2.0",
"splines": "ortho",
"nodesep": "0.65",
"ranksep": "1",
"fontname": "Sans-Serif",
"fontsize": "11",
"fontcolor": "#2D3436",
}
node_attrs = {
"shape": "box",
"style": "ortho",
"fixedsize": "true",
"width": "1.5",
"height": "1.5",
"labelloc": "b",
"imagescale": "true",
"fontname": "Sans-Serif",
"fontsize": "11",
"fontcolor": "#2D3436",
}
edge_attrs = {
"color": "#7B8894",
}
with Diagram("architecture", show=False, graph_attr=graph_attrs, node_attr=node_attrs, edge_attr=edge_attrs):
# Queue
recommend_system_api_queue = SQS("Recommendation API Queue")
eks_to_dynamodb_queue = SQS("Processing Queue")
# `main`
with Cluster("Web service"):
router = VPCRouter("VPC Router")
with Cluster("Private VPC") as private_vpc:
with Cluster("Database Cluster"):
db_master = Aurora("Web DB Master")
db_master - [Aurora("Web DB RO")]
with Cluster("Public VPC") as public_vpc:
source = EKS("k8s source : spring boot")
with Cluster("Event Workers"):
workers = [ECS("worker1"),
ECS("worker2"),
ECS("worker3")]
source >> workers
workers >> router >> db_master
workers >> recommend_system_api_queue
workers >> eks_to_dynamodb_queue
# `rec_sys`
with Cluster("Recommendation API") as rec_sys_vpc:
cvpn = ClientVpn("ec2 client VPN")
s3_model = S3("Trained Model")
with Cluster("Private VPC"):
with Cluster("Train & Optimize"):
dynamodb = Dynamodb("Training Data")
sagemaker_training = Sagemaker("Training")
with Cluster("Deploy"):
sagemaker_hosting = Sagemaker("Hosting")
rec_sys_workers = [
Lambda("API with golang"),
Lambda("API with golang"),
Lambda("API with golang"),
]
with Cluster("Processing"):
processing_workers = [
Lambda("processing worker"),
Lambda("processing worker"),
Lambda("processing worker"),
]
dynamodb >> sagemaker_training >> s3_model >> sagemaker_hosting
recommend_system_api_queue >> rec_sys_workers >> sagemaker_hosting
eks_to_dynamodb_queue >> processing_workers >> dynamodb
# `local`
with Cluster("Local RTX3080 Linux"):
local_linux = Ubuntu("Local training PC")
local_linux >> cvpn >> dynamodb
local_linux >> s3_model
# `frontend`
with Cluster("Frontend"):
bucket_vue = S3("vue project")
cloudfront = CloudFront("Cloudfront")
client = Mobile("Web")
bucket_vue >> cloudfront >> client >> workers
졸업 프로젝트에서 이런 다이아그램을 제시하니 임팩트가 있었던 것 같다. (사실 별거 없지만..)
일관된 테스트 환경 구성
나는 macOS 를 사용하지만, 내 파트너는 Windows OS를 사용하였다. local 환경에서 각자 웹서버를 실행하기엔 OS 차이로 인해 마음이 편하지 않았다. Windows OS에서는 Docker 를 사용했을 때 매우 까다롭기 때문에 vagrant & virtualbox 를 이용하여 linux vm 끼리 통신하는 방법을 채택했다. (웹 백엔드, 추천시스템 백엔드, 데이터베이스, 웹 프론트엔드)
실제 프로덕트를 구현하는 일과 같은 중요도로 데이터베이스 관리 툴과 프로비저닝 툴을 개발했다. 지금 당장은 시간을 많이 잡아먹는다고 생각할 수도 있지만. 앞으로 반년을 더 달려야 하기 때문에! 결과가 좋다면 프로덕트 런칭까지도 생각하고 있기 때문에 이렇게 했다.
🤯절망편 — 중간고사까지 쳐내고 나서 느낀 점.
우리는 아주 게으른 존재다. 생각보다 많은 개발을 하진 못했다. 회사 일과 대학원 진학을 위한 전형 진행, 시험기간까지 이겨내야하는 상황이었기 때문에 겨우겨우 추천 알고리즘을 사용한 API를 서빙하는 간단한 MVP를 만들어 낼 수 있었다.
아주 작은 규모의 개발팀이지만 효율과 문화를 동시에 생각하면서 팀을 운영하는게 아주 어려운 일임을 이번 프로젝트를 진행하면서 느꼈다.
1학기동안 아쉬웠던 점.
IaC (Infrastructure as Code)를 달성하지 못해 아쉬웠다. 우리의 구현체를 배포하는데 웹 콘솔을 이용하는 점, 지금 당장은 매우 아쉽지만 방학과 2학기 때는 golang 으로 도전해볼 생각이다.
추천 시스템 머신러닝 학습과 그 구현에 많은 시간을 썼다. 그 덕에(?) 웹 클라이언트가 매우 지저분하게 작성되었다. 2학기 때 뜯어고칠 생각이다.
후속편이 작성될 수 있게 프로젝트를 성공적으로 마무리할 수 있었으면 좋겠다. =]