try:개발, catch:멘붕, finally:성장 – 문과생의 개발자 회고록

kyumerican0·2025년 5월 11일
post-thumbnail

Contents

  1. 이 회고록을 쓰는 이유는...

  2. 기자를 꿈꾸던 고등학생, 공대에 들어오다.

  3. 개발...생각보다 재밌는데?

  4. 그러므로 시작된 풀스택 찍먹 여행기

  5. 새로운 도전, 그리고 멘붕의 시작

  6. 그리고 삶을 바꾼 선택, Mohey
    A. 짠, 내가 나누어져볼게! - MSA 적용기
    B. 빠르게! 더욱 빠르게!! - 채팅 cache 적용기
    C. 쿼리야...이제 좀 쉬어도 돼... - N+1 문제 해결기
    D. 안뉘 사람들이 왜 이렇게 많이 와? - 대규모 트래픽 경험 및 ALB, ASG 이동 일기
    E. 새벽에 일어난 DoS 랜덤 디펜스 - 아제르바이젠발 DoS 공격 대응기

  7. 음악 밴드와 어벤져스: 개발자로서의 가치관

  8. 그리고, 지금.


0. 이 회고록을 쓰는 이유는...

"문과 출신 개발자로서 어떤 삶을 살아왔는지 돌아보자!"

...라는 목적만으로 쓰는 글은 절대 아닙니다ㅋㅋㅋㅋ

감사하게도 25년도 팀네이버 공채에 1차 면접을 합격하고, 지금으로서는 최종 면접을 준비하고 있습니다. 자소서에 이것 저것 꼬리 질문을 달며 대답하고, CS 지식도 다시 훑어보고, 1차 면접 복기도 하며 준비를 하던 도중, 다음과도 같은 질문이 머릿속에 떠오르게 되었습니다.

그런데... 나 인성 면접 준비는 되어있나?

지금까지 기술 면접만 봤었던 전 인성 면접을 본 경험이 전무합니다.

최종 면접까지 간 경우도 기술 면접만을 봤었던 저에게, 인성 면접이란 알려지지 않은 미지의 버뮤다 삼각지대와 같은 존재였습니다. 면접 준비를 함과 동시에 인터넷에서 인성 질문들을 찾아보고 주변 사람들에게 물어보며 대충 인성 면접에 대한 감을 잡아가며 시간을 보냈습니다.

어느 정도 정리가 된 후, 다음과 같은 생각이 들었습니다.

나 자신을 정말 솔직하게 보여주고 싶다!

인성 질문들은 '나라는 사람이 어떤 사람인지' 물어보는 경우가 많았고, 이러한 질문에 대해서 '나라는 사람'을 잘 보여주고 싶었습니다. 알려지지 않은 버뮤다 삼각지대에 대한 두려움보다는, 미지의 영역에 대한 호기심과 '나라는 사람'을 잘 보여주고 싶다는 생각이 더 컸습니다.

그래서, 인성 면접도 준비할 겸, '나라는 개발자는 어떤 삶을 살아왔고 어떤 경험을 해왔으며, 어떤 가치관을 쌓아왔는가'에 대한 회고록을 적어보려고 합니다. 그리고 면접 준비라는 생각보다는, 최종 면접 전 '쉬어가는 시간'이라고 생각하며 재미있게 적어보려고 합니다.
(실제로 조금의 휴식이 필요하다고 생각이 되는 시점이기도 하고요...좀 많이 지쳤으...)

한번 시작해볼까요.
기자를 꿈꾸던 한 고등학생이, 어떻게 서버 개발자가 되었는지

제 회고록을 적어보도록 하겠습니다. 😎😎

필자는 글을 못 줄인다는 특징이 있습니다...ㅋㅋㅋ 글이 조금 길고 장황하더라고 넒은 마음으로 봐주시면 감사하겠습니다 :)

만약 개발자로서의 제가 어떤 사람인지 궁금하다면, 제 포트폴리오를 확인해주세요! :)


1. 기자를 꿈꾸던 고등학생, 공대에 들어오다.

너가 문과였다고?

마치 정해진 관례처럼, 어느정도 친해진 사람들에게 꼭 한번씩은 듣게되는 말입니다.

하지만 저는 기자를 꿈꾸던 문과생이었습니다.
해외 소재 한국학교에 다니던 시절 시절, 저는 '신문부 부원 \rightarrow 신문부 편집장 \rightarrow 신문부 부장'이라는 테크트리를 탄, 누가 봐도 기자가 꿈인 학생이었습니다.

그래서, 어떤 기자를 하고 싶은데?

당시, 저에게는 2가지 방향의 기자의 꿈이 있었습니다.
1. 전쟁의 부조리함과 참혹함을 전달하며 전쟁의 끔찍함을 알려주는 종군기자
2. 계속해서 발전하는 IT에 대해 사람들에게 더욱 쉽게 알려주는 IT 기자

방향성이 정말 많이 다른...ㅎㅎ 2가지의 꿈이었는데, 당시 학교 수업 수행평가로 4차 산업에 대해서 조사를 하며 IT에 관심을 갖게 된 것이 컸습니다. 그리고 종군기자보다 현실적이고 가능성이 높다는(...ㅎ) 주변 사람들의 말로 인해 IT관련 부서에서 근무하는 기자를 장래희망으로 잡았습니다.

그러던 중, 인생 전체를 바꾸게 될 정보를 보게 됩니다.

문과생을 뽑는 공과대학이 있다고?

네, 정답입니다.
당시 한양대학교 정보시스템학과라는 공과대학 (현재 소프트웨어대학) 소재의 학과가 존재한다는 말을 들었고, 경영 + IT학과 이야기를 듣고 곧바로 지원을 하였습니다. 학교차원에서 IT를 배우는 것이 IT 기자가 되기 위한 포석이라고 생각하면서 말이죠.

그렇게 한양대학교에 합격을 하고, 반쪽짜리 공대생으로 한양대학교에 들어오게 되었습니다. 제가 앞으로 어떤 길을 걷게 될지 알지 못한 상태로 말이죠.


2. 개발...생각보다 재밌는데?


(씨익)

그렇게 한양대학교 정보시스템학과로 입학을 하고, Forif라는 동아리에 프로그래밍 동아리에 들어가게 되었습니다. (현재는 중앙동아리지만, 그때는 미니미 과동아리였습니당...추억쓰)

그리고 저에게 있어서 첫 언어였던 Python과 만나게 되었죠.

(지금은 사용하지 않지만... 첫 언어였다는 점에서 의미가 컸던 언어)

동아리 선배들은 Github 사용법도 몰라 Google 공유 Drive로 협업을 시도했던 나에게 하나하나 프로그래밍에 대해서 알려주었고, 개발에 대해서 차츰차츰 감을 잡아갔습니다. 1학기 말에는 과 동기들과 함께 python으로 한양대 근처의 맛집을 소개해주는 '한슐랭 가이드' 위젯을 개발하여 해커톤에 출마할 정도로 성장했습니다.

어떤 계기로 개발자라는 진로를 갖게 되었나요?

이러한 질문에 전 자신있게 대답할 수 있습니다. "정말, 정말로 개발이 재미있었다."라고요!

저는 어렸을 때부터 '만드는 것'을 좋아했습니다.

'내 손으로 세상에 없던 무언가를 만들어내고 싶다!'라는, 꿈 많은 청소년의 소망과도 같은 생각을 갖고 살아갔었습니다. 음악을 만들어내고자 작곡 동아리를 운영하기도 했었고, '이런게 있으면 사람들에게 도움이 되겠다!'라는 생각으로 이런 저런 발명 대회에 참여하기도 했습니다.
(만약 제가 그림을 잘 그렸다면, 그림 쪽으로 진로를 틀었을수도 있었을 것이라고 생각하기도 합니다 ㅎㅎ)

그런 저에게 개발과 프로그래밍은 '무언가를 내 손으로 창조할 수 있는 방법'으로 다가왔습니다. 제 아이디어를 실제로 구현하고, 실제로 '세상에 없던 무언가'를 만들어낼 수 있는 수단으로요.

뭐? 노트북 한 대로 내 머릿속의 아이디어를 실제로 구현해 낼 수 있다고?
그리고 그 구현해 낸 것을 사람들이 실제로 사용할 수 있다고?

새로운 미지의 영역인 '개발'이라는 분야를 더 파고들지 않을 수가 없었습니다.


3. 그러므로 시작된 풀스택 찍먹 여행기

뭐가 맛있을까!!

이후에 여러 분야의 개발을 시도해보았습니다.
마치 맛있는 뷔페에서 뭐가 맛있을지 기대하며 골라먹는 것처럼, '내가 경험하지 못한 개발 분야에서는 어떤 즐거움을 느낄 수 있을까!'라는 생각으로 여러 분야에 도전을 해보았습니다.

  • 프론트엔드 (Frontend)
    • 개인적으로 React를 공부하며 여러 웹페이지를 만들어보기도 하며 프론트엔드 기본기를 다져갔습니다. 지금은 리뉴얼 된 동아리 웹 페이지 개발을 시작으로, 이런저런 웹페이지 개발을 맡기도 했으며, 나중에 실력이 쌓였을 때는 이런저런 작은 웹페이지 개발 외주를 맡으며 용돈 벌이를 했습니다.

  • 백엔드 (Backend)
    • 프론트엔드를 공부하며 자연스럽게 그 뒷단에서 돌아가는 백엔드도 궁금해졌었고, 서버에 대해서도 공부를 하기 시작했습니다. 데이터베이스와 연결하여 간단한 웹페이지의 서버를 만들기도 했고, 웹소켓을 활용한 채팅 서비스, 날씨를 알려주는 서비스 등을 만들며 백엔드에 대한 지식을 쌓아갔습니다.
    • 이후 스타트업의 백엔드 개발자로서의 인턴십 역시 갖으며 실무 지식을 채워나갔습니다.
    • 이후에 이야기하겠지만, 저는 백엔드를 공부하며 너무나도 재밌었습니다. 제가 서버 개발자로 결정하게 된 이유 중 가장 큰 이유가 "재미있어서"라는 단순한 이유이기도 하죠.

  • 애플리케이션 (Application)
    • Java와 Android Studio를 사용하여 개발을 하게 되었습니다. 당시 Naver Boostcourse에 참여하며 안드로이드 앱 개발을 수강하며 공부를 했었던 기억이 납니다. 이후 React Native 역시 활용을 하며 Cross-platform application을 경험하기도 했습니다.
    • Forif 동아리에서 '안드로이드 앱 개발'이라는 스터디를 열어서 학생들을 가르치기도 하며 이런저런 해커톤에 나가기도 했습니다. (정말 부족했을 멘토였는데, 잘 따라준 멘티들이 지금도 감사하네요ㅎㅎ)

  • 게임 (Game)
    • C#으로 Unity 기반 게임을 개발하는 스터디에 참여하며 게임 개발을 하기도 했었습니다. 플랫포머, 탄막 슈팅 등의 게임을 개발하며 프로그래밍에 대한 재미를 쌓아갔습니다. 회고하면서 기억났는데, 게임 개발으로도 해커톤에 참여하기도 했네요. 캐릭터 공격 로직과 보스 디자인을 설계하며 재밌어했었던 기억이 납니다ㅎㅎ

지금 회고를 하면서도, "어떻게 저걸 다 했지...?"라는 생각이 드는 대학생 1~2학년 생활이었습니다...ㅎ 정말 개발에 대한 재미로 살아갔던 새내기 생활이었다는 생각이 드네요.

이런저런 개발을 하면서 결국 제가 제일 '맛있다!'라고 느꼈던 분야는, 위에서도 언급했듯이 백엔드, 즉 서버 개발이었습니다. 그 이유는 다른 분야보다 더 재미있었다는, 실로 단순한 이유입니다.
여러 이유가 있겠지만, 저는 "유저의 요청을 처리 및 반환하는 로직을 설계하고, 데이터의 흐름을 직접 다루는 것"에 큰 흥미를 느꼈던 것 같습니다.

그렇게 백엔드로 방향을 잡고 개발자로의 진로를 결정하였습니다. 그리고, 이 선택은 정말 잘한 선택이었죠.


4. 새로운 도전, 그리고 멘붕의 시작


경영 + IT를 배운다는 점은 양날의 검입니다.

둘 모두에 해당하는 지식을 얻을 수 있지만, 둘 모두에 해당하는 지식이 모두 얕다!

저는 조금 더 깊은 IT, 더 나아가서 깊은 CS 지식을 배우고 싶었고, 설상가상으로 경영은 저와 너무나도 맞지 않았습니다. 그래서, 2학년 2학기에 다시 한 번 저는 큰 도전을 하기로 결심하였습니다.

컴퓨터 소프트웨어학부로 다중 전공을 하자!

한양대학교는 다른 대학교와 같이 다중 전공 (혹은 복수 전공)을 할 수 있었고, 다행히도 학교 공부를 게을리하지 않아 다중 전공이 가능한 학점을 가지고 있었습니다. 그리고 다중 전공을 한다면 저와 맞지 않는 경영 관련 전공을 최소화해서 듣고, 그 전공을 CS관련 전공으로 채울 수 있다는 점이 가장 큰 매력이었죠.

다행히 한양대학교는 다중전공을 허락해주었고, 저는 정보시스템학도이자 컴퓨터소프트웨어학도로 생활할 수 있었습니다.


그리고, 저의 멘붕이 시작되었습니다.

안녕? 난 번아웃이라고 해!

컴퓨터 소프트웨어학부에 적응할 틈도 없이, 불청객이 저에게 찾아왔습니다. 개발도, 공부도, 마치 제 안의 엔진이 꺼진 것처럼 아무것도 되지 않았습니다. 재미있어서 하는 것이 아니라, 저에게 닥친 일과 저에게 주어진 책임을 겨우겨우 해내는 지경에 이른거죠.

본격! 하기 싫은 일 랜덤 디펜스!

당시 Forif의 부회장으로서의 책임이 점점 무겁게 느껴졌고, 점차 무너지고 있을 즈음, 저에게 있어서 멘토와도 같은 Kang형이 조언해주었습니다.

너가 할 수 있는 '최대한'이 아니라, '최소한'을 생각해.
그리고 그 최소한을 달성한다고 생각해.
그리고 그 최소한을 못한다고 생각할 때, 더 최소한을 생각해.
중요한 것은, 그럼에도 불구하고 조금씩 해나가는거야.
그리고 남는 시간에 제발 좀 쉬고.

그리고 Eun누나의 조언도 기억나 적어봅니다.

너가 달성하는 무언가가 너를 정의하는게 아니야.
너가 이루는 무언가가 너를 증명하는게 아니야.
너의 소중함과 의미는, 너의 존재 자체로도 증명이 된다는거를 기억했으면 좋겠다 :)

이외에도 정말 많은 조언을 받았었고, 정말 많은 위로와 도움이 되었습니다. 항상 제가 할 수 있는 최대한만을 생각하며 달려왔던 저에게, 최소한을 알려준 조언이었습니다. 사실 정말 쉬운 정답이었는데, 그 쉬운 정답조차 잘 알지 못한 저에게 방향을 제시해준 여러 사람들에게 다시 한 번 감사하네요. :)

결국 번아웃을 극복하고, 개발에 대한 재미 역시 다시 회복했습니다. 이후 만족할만한 학점으로 2학년을 마무리하고 국가의 부름을 받아 군복무를 떠났습니다.

사실, 번아웃 뿐만이 아니라 인간관계를 포함한 여러 방면에서 많이 힘들었고 크게 절망했었던 시기였습니다. 하지만 더 이상 자기 연민에 빠지지 않기로 결심했고, 회고록과는 맞지 않아 많은 부분을 생략했습니다.
암흑기라고는 생각하지 않습니다. 이 시기가 있었기에 지금의 제가 있는 것이라고 생각합니다. 🙂🙂


5. 그리고 삶을 바꾼 선택, Mohey

모여서 헤이! Mohey!

그렇습니다. 창업에 대한 이야기입니다. 지금까지의 제 삶에서 가장 베스트의 커밋 중 하나이죠.
전역 전부터 학교 동기들과 만나며 동기들이 계획하고 있는 창업에 대해서 듣게 되었습니다.

외국인들과 한국인들이 쉽게 만나서 놀고 교류할 수 있는 플랫폼을 만들자!

그리고, 저는 자연스럽게 이 팀에 합류하게 되었고, 부족한 실력이지만 무려 CTO로서 해당 창업 팀, 위라클에서 근무하며 모헤이 (Mohey)앱을 개발하게 되었습니다.

이때, 개발자로서 정말 소중한 경험을 하였고, 이 시점을 기준으로 저는 다시 한 번 개발자로서 점프를 하게 되었습니다. 그 중 다섯 가지의 소중한 경험들을 회고해보고자 합니다. (지금은 사라진 서비스니, 자세한 서비스 로직과 하지 못했던 이야기들을 주구장창 풀어보려고 합니다ㅎㅎ)

A. 짠, 내가 나누어져볼게! - MSA 적용기

군대에 있을 때부터 MSA에 대해 알게 되었고, 도메인 분리, 비동기 처리 등등을 공부하게 되면서 MSA에 대한 관심이 높아지게 되었습니다. 해당 서비스를 기획하고 설계하는 시점부터 합류하게 되어서, MSA를 기반으로 서버를 설계해보기로 했습니다.

사실, 스타트업에게 완전한 Full-MSA 설계는 과합니다. MVP를 빠르게 만들고 테스팅을 해야하는 스타트업에게는 모놀리식이 더욱 어울리는 편입니다.

하지만, 저희 팀은 다음과 같은 이유로 MSA의 구조를 도입하기로 결정했습니다.

  • 스타트업 특성상 업데이트가 잦은데, 업데이트 할 때마다 전체 서비스를 재배포 하는 것이 아니라, 관련 도메인만 재배포 할 수 있다는 점
  • 결제, 이미지 업로드와 같이 오래걸리고 작업량이 많은 서비스에 대한 비동기 처리를 할 수 있다는 점
  • 자주 변하는 데이터베이스 스키마에 대해서 기존 서비스와 decouple하여, 유연한 인터페이스를 제공할 수 있다는 점 (graphQL 적용)

처음에는 다음과 같은 MSA로 서비스를 설계를 하였습니다.

  • 메인 서버
  • 데이터베이스 서버
  • 이미지 처리 서버 (AWS Lambda)
  • 결제 처리 서버

이후 메인 서버에서 추가로 채팅 서버를 분리하여 배포를 하여, 총 5개의 도메인 컴포넌트로 이루어진 MSA로 설계를 하였습니다.

이를 실제로 운영하기 위해 Redis PubSub을 활용한 Notification Broadcasting, RabbitMQ를 활용한 컴포넌트 간 비동기 처리, 이후에 서술하겠지만 Redis를 활용한 캐싱 등등 정말 많은 것을 학습할 수 있었습니다.

물론, MSA를 설계하고 배포하며 다음과도 같은 농담이 뼈 아프게 다가왔습니다.

WA! 1개의 거대한 문제가 37개의 조그마한 문제로 나누어진다고?? MSA 마법은 정말 대단해!!

어느새 하나의 큰 버그가 아니라 여러 개의 분산된 작은 버그들과 싸우고 있는 저와 팀원들을 발견하게 되었습니다. 로컬 테스팅에서 알 수 없는 버그가 발생하면, 팀원들과 잠시 커피를 사온 뒤 한숨 한 번 쉬고 하나 하나 로그를 까보았던 기억이 새록새록하네요ㅎㅎ

그래도 하나 건진 것은 확실히 빨라지고 유연해진 배포였습니다. 전체 서비스를 재배포할 필요 없이 원하는 서버만 재배포하면 업데이트가 이루어진다는 점이 꽤나 쏠쏠하게 다가왔습니다.

그리고 장애 격리 역시 MSA 설계로 얻을 수 있던 이점이었습니다. 별도의 프로세스와 배포 단위를 갖춘다는 것은 물리적이고 논리적인 '격리'단위를 얻는다는 것과 같습니다. 해당 서비스를 운영하며 크고 작은 장애를 많이 겪어보았는데, MSA 설계로 인해 장애가 격리가 되어서 피해가 최소화되지 않았었나, 지금 회고해봅니다.

B. 빠르게! 더욱 빠르게!! - 채팅 cache 적용기

베타 테스트 후, 서비스 정식 런칭 일주일 전에 부하 테스트를 하던 도중, 큰 문제를 발견했습니다.

아니 채팅 로그 50개밖에 안 쌓였는데, 왜 이렇게 느려져?

채팅방의 채팅 수가 약 50개가 넘어가면 가시적으로 채팅의 로드 속도가 느려지는 현상이 발견되었던 것입니다.

DB 로그를 보고, 병목이 생기는 지점이 있나 판단하던 중, 다음과 같은 로직에서 가장 가능성있는 문제점을 발견했습니다.

  1. 사용자가 채팅방으로 입장합니다.
  2. 채팅방에 기록이 되어있는 모든 채팅 로그를 불러옵니다.
  3. 해당 채팅 로그가 한번에 로드됩니다.
  4. 사용자가 채팅방에 재입장을 할 시, 모든 채팅 로그를 다시 불러옵니다.

워낙에 급하게 만들었던 기능이라고 해도...당시에 로직을 보고 팀원들과 함께 '너무 대충 개발했다'라며 반성을 했습니다. 속도 저하가 발생할 수 밖에 없는 당연한 구조였던 것이죠.

업무 분담!

프론트엔드 팀에게는 Pagination을 적용하여 한번에 모든 채팅 기록을 불러오지 않고 유저가 일정 이상 스크롤을 하면 그 때 나머지 채팅 로그를 불러오도록 개발을 해달라고 요청을 했습니다.

백엔드 단위에서도 문제 해결이 필요했습니다. 특히 4번에 대해서 백엔드 단위에서 Cache Layer를 도입함으로 해결을 해보기로 결심을 하였죠.

Cache layer를 적용하는데 있어 제가 고려한 점은 다음과 같습니다.

  1. 읽기를 포함한 데이터베이스의 접근을 최소화
  2. Cache Miss의 최소화
  3. 구현의 난이도
  4. RAW (Read-After-Write) Consistency 보장
  5. High-read, High-write (Read, Write 모두 빈번하게 일어나는 데이터)에 알맞는 캐시

이러한 점을 모두 고려하였을 때, 제가 선택한 방안은 Write-through cache였습니다.

  • Cache-Aside: Cache Miss가 발생 시에만 캐시에 데이터를 write하는 전략입니다. 처음 요청에 대해서는 무조건 cache-miss가 발생하여, DB를 한번 찌른다는 점에서 1번 조건과 2번 조건이 위배된다고 판단했습니다. 일부 cache miss가 허용되거나 Write가 조금 일어나는 경우에는 적절한 선택이겠으나, 제가 원하는 조건은 아니기에 패스했습니다.

  • Write-through: 먼저 cache에 write한 후 동시에 DB에도 write하는 방법으로서, Read와 Write 모두 빈번하게 일어나는 데이터에 어울리는 캐시입니다. 캐시에 있는 데이터는 항상 최신 데이터임이 보장되기에 RAW Consistency 역시 보장이 되는 전략입니다. 위의 조건을 모두 만족함으로 해당 전략을 선택했습니다.

  • Write-back: Cache에만 데이터를 저장하고 있다가, 데이터가 변경이 되었을 때 (dirty) 데이터베이스에 저장하는 전략입니다. Write-through보다 더욱 빠른 속도를 보장하지만, 구현이 까다롭고 일관성을 보장하기 어렵다는 점에 있어서 3,4번 조건을 위배한다고 생각하여 패스했습니다.

Redis를 기반으로 Write-through cache의 구현하였으며, 자세한 워크플로우는 다음과 같습니다.

  1. 채팅 로그를 병목 지점이었던 50개를 기준으로 구분, Redis에 저장합니다. (이때, 데이터의 구조는 List)
    이때, key는 chat:room:{roomId}:{n}형식으로 합니다.

  2. 채팅 로그가 생성될 때, 해당 메시지를 Redis의 가장 최신 세트에 추가 (LPUSH), LTRIM 0 49으로 50개의 데이터를 유지합니다. (최신 50개의 데이터만 유지합니다.)
    이때, 최신 50개의 채팅 데이터는 사용자가 채팅방에 진입할 때 가장 먼저 접근하는 데이터이므로, 긴 TTL를 설정합니다. 저는 12시간으로 설정했습니다.
    새로운 메세지가 형성이 되어 write가 될 때마다 TTL을 refresh합니다.

  3. 이후 해당 새로운 로그를 RDB에 저장합니다. (Write-through)

  4. 사용자가 과거 메시지를 요청해 cache miss가 발생할 경우, DB에서 이전 50개 메시지 세트를 조회합니다.
    해당 세트를 Redis에 임시로 해당 리스트를 캐싱합니다. 이 경우에는 짧은 TTL을 설정합니다. 저는 5분으로 설정했습니다.
    이때 key는 chat:room:{roomId}:{n+1}로 합니다.
    (만약, chat:room:{roomId}:{0}에 해당하는 key가 존재하지 않으면 긴 TTL을 설정하고 해당 key로 caching을 진행합니다. 이 경우는 기본적으로 캐싱 되어있는 50개에 대한 채팅 로그의 TTL인 12시간이 지나 cold miss가 발생한 케이스라고 판단합니다.)

  1. 메시지 조회는 DB가 아닌 Redis를 최우선으로 조회합니다.
    (chat:room:{roomId}:{0} chat:room:{roomId}:{1}, ... , chat:room:{roomId}:{n})

  2. Redis가 다운되어 데이터가 사라질때를 대비하여 DB에서 채팅방별 최신 채팅 로그 50개를 캐싱해오는 /refresh 엔드포인트를 만들어 Cache pre-warming을 가능하도록 만들었습니다.

별도의 Persistence 설정은 하지 않았습니다. Redis가 다운되었을 시, DB를 바탕으로 cache rebuild를 진행하면 된다고 판단했습니다. 지금 생각해보면 RDB와 같이 주기적으로 Disk에 백업을 해두는 방법은 warm cache를 사용할 수 있다는 점에 있어서 고려할만하다고 생각이 되네요.

일단 해당 방법과 Frontend 팀에서 구현한 scroll 기반 pagination을 적용하고 보니, 채팅 로그와 상관없이 불러오는 속도가 개선이 되었습니다. '겉으로 보기엔' 문제는 해결이 된 것처럼 보였습니다. '겉으로는요'....

지금 보면 참으로 허점이 많은 캐싱입니다. DB write가 실패했을 시 cache에 ghost 데이터가 남게 되고 해당 데이터를 복구할 수 있는 방법이 없다는 점이 가장 큰 허점이네요. 이 경우 Redis에 데이터가 추가되는 것을 CDC로 삼거나 Outbox Pattern을 활용하여 Eventual Consistency를 조금 더 확실하게 만드는 방법으로 해당 작업의 atomicy를 보장해야할 것 같습니다.


C. 쿼리야...이제 좀 쉬어도 돼... - N+1 문제 해결기

자, 이제 근본 문제를 해결해볼까요!

제 마음에 가장 걸렸던 것은 DB 로그였습니다. ㅇ
분명히 요청되는 것은 전체 채팅 로그 호출 하나인데, DB 로그는 수십~수백개가 한번에 쫙쫙 올라가는 것이었습니다.

당시 DB 서버는 GraphQL로 구성이 되어있었습니다. 유저가 한 채팅방의 전체 채팅 로그를 불러올 때, 다음과 같은 일련의 작업이 일어나게 됩니다.

  1. 기본 채팅 정보를 불러옵니다.
  2. 각 채팅에 대한 유저 정보를 불러옵니다.
  3. 각 채팅을 읽은 유저 수를 불러옵니다.
  4. 모든 채팅에 대하여 해당 유저의 읽음 처리를 합니다.

벌써 냄새가 나죠? ㅎㅎ

대충 graphql의 query 구조는 다음과 같게 됩니다.

query {
  chatLogs(chatRoomId: 1) {
    id
    payload
    imgUrl
    createdAt
    repliedTo
    user {
      id
      name
      profileUrl
    }
    readCount
  }
}
// readCount - chatLogRead 테이블 조회 후 count

이때, 저는 각 유저와 readCount 각각에 해당하는 field resolver가 있었고, 각 field resolver는 해당하는 ORM의 query가 있었습니다.

네, Query가 또 Query를 만들게 되는거죠.

쿼리가 복사가 된다고?!

즉, 유저가 한 채팅방의 전체 채팅 로그를 불러올 때 불러와지는 쿼리의 개수는

  1. 기본 채팅 정보를 불러옵니다. (1)
  2. 각 채팅에 대한 유저 정보를 불러옵니다. (N)
  3. 각 채팅을 읽은 유저 수를 불러옵니다. (N)
  4. 모든 채팅에 대하여 해당 유저의 읽음 처리를 합니다. (N)
    \Rightarrow 3N + 1

네... 쿼리가 복사가 됩니다. 예를 들어, 채팅 로그를 100개 호출하는 1개의 쿼리를 만들었을 때, 총 301개의 쿼리가 만들어지는 대환장파티가 만들어지게 되는 것이죠.

Cache도 없었던 시절에는 어떻게 이 부하를 견뎠을까... DB야 고생했다....

원인을 알았으니, 문제 해결은 간단했습니다.
우선 사용했던 ORM(Prisma)의 문서 정독 후, include와 같은 필드를 활용해서 데이터를 추가로 fetch해서 불러오는 것이 아니라 Join해서 불러오도록 했습니다. 이 방법으로 다른 db resolver들도 개선을 했구요.

또한 읽음을 처리하는 로직을 별도의 API, 별도의 resolver로 분리했습니다. 전혀 같은 로직에 있을 필요가 없는 로직이었는데, 왜 하나의 query로 만들었을까 싶네요.

이러한 방법으로 N + 1 문제까지 해결했습니다!

일주일 후, 개선된 채팅 서비스와 함께 성공적으로 서비스를 런칭하였습니다. 배포 후 접수된 불만 사항 중 채팅과 관련된 문제는 극소수였고, 그 중에서도 채팅 속도에 대한 불만 사항은 없었다는 점에서 꽤나 고무적인 결과를 가져왔다고 생각이 되네요. 😎😎

D. 안뉘 사람들이 왜 이렇게 많이 와? - 대규모 트래픽 경험 및 ALB, ASG 이동 일기

오전에 개발, 오후에 오프라인 홍보, 저녁과 밤에 또 개발. 정말 함께하는 팀원이 아니라면 버틸 수가 없었던 일정이었습니다. 지금 생각해봐도, 당시에 팀원들과 함께 이 일을 즐겼기 때문에 가능한 일정이었던 것 같습니다.

평소와도 같이 오프라인 홍보를 하던 날이었습니다. 당시 가입 후 일정 미션을 수행하면 상품을 주는 이벤트를 진행하고 있었고, 이를 홍보하고 있었습니다. 어느 한 인도네시아 분이 우리 서비스를 보고 "유학생 입장으로서 너무 필요한 서비스에요!"라고 이야기를 하며 좋아하며 이벤트에 참여했던 것이 기억납니다.

그 인도네시아 분이 유명한 틱톡커였을줄이야.

그 분은 '감사하게도' 저희의 서비스를 본인 틱톡 계정에 홍보해주었고, 트래픽의 폭주는 얼마 뒤 시작되었습니다. 당시 모니터링 체계를 따로 구축하지 않아 정확한 요청 수를 판단할 수 없었지만, CassandraDB에 쌓이는 로그의 수로 어림잡았을 때 RPS 1000~2000 사이의 요청 수가 쏟아지기 시작했습니다.
(대규모...라고 하기에는 부끄럽긴 하지만 제가 경험한 트래픽 중 가장 대규모에 가까운 양이었습니다)

당시 메인 서버 2대가 EC2에 배포가 되고 있었으며, Nginx 기반 리버스 프록시 서버가 서비스의 Entrypoint로 작동을 하며 메인 서버로 로드 벨런싱을 진행하고 있었습니다. 다행히 해당 요청들을 잘 감당하는 두 대의 메인 서버가 참 대견하게 여겨졌습니다.

그런데 불현듯, 다음과 같은 불길한 예감이 떠올랐습니다.

그런데, 이 리버스 프록시가 다운되면, 서비스 전체가 다운되는거 아니야?

아불싸, 예상치 못한 곳에 SPOF가 있다는 것을 대규모 트래픽이 몰리는 시점에 발견을 하게 되었습니다. 다른 팀원이 홍보를 떠난 그 날, 저는 사무실에 남아서 조마조마해하며 모니터링을 진행했던 기억이 나네요. 😅😅

어느 정도 트래픽이 진정된 후, AWS를 조금 더 파고들었습니다. 그리고, ALB와 ASG를 활용한 오토스케일링에 대해서 알게 되었죠.

뭐? SPOF가 아닌 로드벨런서가 있다고? 그리고 자동으로 Scaling도 해주고, Service Registry로 진행해준다고?

지금까지 ASG 기능을 사용하지 않았던 점이 바로 Service Registry를 어떻게 할지가 애매해서였는데, ALB 타겟 그룹에 자동으로 register/deregister를 진행한다니,AWS 공부가 많이 부족하다고 생각이 되었습니다.

기존 Nginx 기반으로 로드밸런싱을 진행했을 때, 배포 시 서버 업데이트와 프록시 설정을 동시에 관리해야한다는 점 역시 꽤나 불편한 점이었는데, 이때다 싶어 ALB + ASG 기반 오토스케일링 구조로 이전할 계획을 세우고, 큰 무리없이 이전을 성공하였습니다.

그리고 이 때를 기점으로 모니터링의 필요성을 느꼈습니다. CassandraDB의 로그를 보며 모니터링을 하는 것이 너무 고되었기 때문입니다...ㅎ 이때를 기점으로 Prometheus + Grafana 기반 모니터링 체계를 구축하는 프로젝트를 시작했습니다.


E. 새벽에 일어난 DoS 랜덤 디펜스 - 아제르바이젠발 DoS 공격 대응기

새벽 1시경, 집으로 돌아가는 길에 슬랙 알림 소리가 났습니다. 채팅 서버 관련 에러 로그였습니다. 채팅에 지장은 없는 로그였지만, 예상치 못한 에러 로그였기에 곧바로 핫스팟을 켜고 근처 벤치에 앉아 디버깅을 하기 시작했습니다.

고치는데에는 얼마 걸리지 않았고, 재배포까지 한 후 집으로 돌아가 잠을 청했습니다. (CircleCI 기반 배포 pipeline 적용 전이라 직접 push & pull하는 방법으로 배포를 manual하게 진행했습니다.)

갑자기 들리는 슬랙 알림 소리에 잠에서 깨었습니다. 아까 보았던 오류 로그 때문인가, 무언가 쎄한 기분이 들어 곧바로 슬랙 알림을 보았습니다.

  • 경고: 채팅 서버 - CPU 사용률 80% 초과...

아! 머릿속부터 하얘진다는 느낌이 이런거구나!

곧바로 모니터링을 확인해보니 채팅 서버의 CPU가 90% 이상을 사용하고 있다고 비명을 지르고 있었습니다. 심상치 않은 상황에 서비스에 접속하여 채팅을 날려보니 채팅도 보내지지를 않았습니다.

곧바로 CassandraDB에 요청 로그 DB를 뜯어보았고, 하나의 IP에서 초당 수천개의 채팅 쿼리 요청이 날라온 것을 확인할 수 있었습니다. 아니, 분당도 아니라 초당 수천개의 쿼리? 그것도 하나의 IP에서?

누가 봐도 DoS잖아...?

잠깐 멍해졌다가 계속해서 추가되는 요청 로그를 보고 다시 정신을 차리고, WAF에 해당 IP에 대한 차단을 진행했습니다. 그리고 추가 공격을 막기 위해 잠시 채팅 서버를 내렸습니다. 이제 모든 채팅에 대해서는 '서비스 점검 중입니다. 잠시만 기다려주세요'라는 fallback message가 작동할 것입니다.

시계를 보니 새벽 4시, 저에게 주어진 해결 시간은 길어봤자 3시간이었습니다. SSH로 급하게 채팅 서버의 컨테이너로 접속을 하고 코드를 보았을 때, 경악을 하지 않을 수 없었습니다.

있으면...안되는게 있잖아...?

여러가지 디버그 로그들, 아직 공개되지도 않은 미출시 기능에 대한 함수, 더 나아가 하드코딩된 환경 변수까지...
저는 화들짝 놀라 전날에 작업한 작업물을 확인해보았습니다. 그리고 원인을 알 수 있었죠.

전날 급하게 벤치에서 디버깅을 하던 저는 배포 환경이 아니라 개발 환경, 즉 온갖 실험적인 로그들과 임시방편들이 난무하는 환경에서 코드를 짜고 있었고, 그것을 그대로 배포 환경에 푸쉬해버린 것입니다. 개발용으로 임시로 하드코딩한 환경변수들, 코드의 흐름을 파악할 수 있는 로그들이 그대로 배포 환경에 노출이 되어버린거죠.

하하... 그리고 약간 적기는 부끄럽지만, 팀원들간의 .env 파일을 따로 받는 것이 귀찮아, 개발 환경에서는 .env를 gitingore하지 않은 상태였습니다...ㅎ

그리고 확인해보니 아니나다를까, container에 .env파일 역시 배포가 되어있는 상태였죠. 심지어 dockerfile 역시 COPY . /app형식으로 모든 파일을 workind directory로 복사한 상태여서, GET요청으로 아주 쉽게 .env를 가져갈 수 있었습니다.

마치 빵집에서 ‘비밀 레시피’라던 크림치즈 케이크를 대문짝만하게 전광판에 띄운 꼴이어습니다.

허겁지겁 노출이 의심되는 환경 변수의 값들은 모두 재발급 받거나 변경하고, .env 파일을 지훈 후, 변경 사항을 production 코드에 적용을 시켜 다시 배포를 하였습니다. 일단 급한 불은 끄자는 생각이고었죠.

다음은 공격 출처와 경로였습니다.

당시 중국, 이란과 같은 국가에서 아무 이유 없이 .env를 GET 요청하거나 의미 없는 요청을 보내는 등의 탐색성 공격이 몇 차례 있었습니다. IP의 대역을 확인해보니 아제르바이잔 국가의 IP라는 것을 확인했습니다. 하필 제가 실수로 개발 환경을 배포 환경에 올린 날에 이 탐색성 공격이 먹혀들어간 것 같다는 판단이 들었습니다.

아무 의미 없이 보낸 탐색성 쿼리에 운 나쁘게 채팅 서비스 관련 주소가 노출이 되었고, 해당 주소로 환경변수 파일을 탈취한 것으로 판단됩니다. 해당 환경변수로 권한을 얻어 메인 서버를 우회하여 채팅 서버에 요청을 초당 수천개씩 보냈다는 판단이 섰습니다.

다음은 가장 두려운, 피해 규모였습니다.

아무리 빠르게 진압을 했다고 해도 초당 수천개의 요청을 적어도 한 시간 이상 받고, 해당하는 로그가 모두 CassandraDB에 때려 박히는 데에 AWS가 적지 않은 요금을 청구할 것이라고 생각이 되었죠.

공격 로그가 모두 AWS EC2 인스턴스에 설치한 자체 Cassandra 클러스터에 하루치 I/O 트래픽으로 기록되었고, 예상보다 높은 EBS I/O 요청 요금이 약 10만원 가량 청구되었습니다. 만약 AWS Keyspaces를 썼다면 요청당 과금 모델로 인해 비용이 훨씬 더 치솟았을 테니, 자체 호스팅 덕분에 오히려 비용 폭탄은 막을 수 있었습니다.

당시 채팅 서버는 오토스케일링의 대상이 아니었기 때문에 과도한 스케일링으로 인한 피해는 없었습니다. 만약 채팅 서버도 ASG로 설정했다면 얼마나 끔찍했을지 상상이 가네요

그렇게 밤을 새고 팀원들에게 전날에 있었던 상황을 공유하며, 10만원어치 저녁을 사겠다고 이야기를 하며 사과를 했습니다. 감사한 팀원들은 커피나 쏘라고 하며, 오늘 하루는 쉬라고 이야기를 했죠. (천사들...)

사후 대처가 가장 중요하다!

이어서 공격이 추가로 들어오지는 않았지만, 이러한 공격을 막기 위해 다음과 같은 작업을 진행했습니다.

  1. 대부분의 서비스 컴포넌트간 통신에 기본적으로 전제되는 것이 '유저가 권한을 얻은 상태'임에도 불구하고, 요청 간 간단한 권한 체크 조차도 없다는 사실을 발견했습니다. 약간의 latency가 발생하더라고 권한이 필요한 각 요청에 대해서 JWT기반 권한 체크를 하는, 간단한 레벨의 ZTA를 구현하였습니다.

  2. Token Bucket 알고리즘 기반의 Rate Limiter를 메인 서버에 달았습니다. 기본적으로 사용자에게 열려있는 Entrypoint인 메인 서버 역시 공격 대상이 될 수 있음을 판단하고, IP와 사용자 ID 기반으로 Rate Limit을 하도록 만들어 기본적인 DoS 공격에 대한 대책을 만들었습니다.

지금 생각해보면, 해당 Token Bucket 알고리즘 역시 경계선 기반 공격 (토큰이 리필되는 시점 전후로 공격하여 정해진 요청보다 더욱 많은 요청을 보내는 공격)에 취약하다고 생각이 되네요. Sliding Window 알고리즘을 더해서 Window 기반으로 트래픽의 양을 조정하면 해당 공격에 대해서도 방지가 되지 않았을까 생각을 합니다.

DoS 공격은 당시엔 정말 살이 떨리는 경험이었지만, 지금 생각해보면 창업했었던 사람만이 얻을 수 있는 소중한 경험이 아닐까 싶네요. 😎😎


6.음악 밴드와 어벤져스: 개발자로서의 가치관

복학을 하고, 창업 활동과 학교 생활을 둘 모두 이어가는 것은 정말 힘든 일이었습니다. 그럼에도 저는 드디어 공부와 개발 모두를 즐길 수 있는 사람이 되었고, 타과생임에도 불구하고 컴퓨터소프트웨어학부 과목 중 하나에서 1등을 하게 되는 고무적인 결과를 얻기도 했습니다.

투자를 받지 못하고, 창업의 속도가 더뎌져 유지보수만 하던 3학년 2학기. 저는 학부연구생 활동을 하며 친해진 친구와 다양한 프로젝트를 진행하게 되었습니다.

그 중 대표적인 것을 이야기하자면,

  • HOLME (홂): MATTER 기술 기반 스마트 홈 서비스 마이그레이션 서비스
  • SHIELDBID: 블록체인과 영지식 증명(ZKP)을 기반으로 한 입찰가 비공개 경매 플랫폼 서비스

새로운 인연도 만나게 되었습니다. 특히 Vin형은 회고글을 쓰는 지금도 서로에게 도움을 주며 취준 생활을 동고동락하는 존재이지요. 이 시점에 소중한 인연들을 너무나도 많이 만나게 되어 감사할 따름입니다. 😁😁

더 나아가 ZKrypto의 백엔드 서버 개발자로 일을 하며 여러 프로젝트에 참여를 하였고, NFC 기반 유저 인증 기능, 대규모 알림 서비스 등을 개발하며 실무 지식 역시 쌓아나갔습니다. 이 모든 성장의 과정 중 힘들도 어려운 경우도 있었지만, 모든 순간이 '재미있었다'라는 점은 부정할 수 없을 것 같습니다.

(아래 사진은 남아공으로 해외 출장을 가 찍은 사진입니다. 제가 참여한 ZKrypto사 프로젝트가 2024 ID4Africa에 소개가 되게 되어 제품 및 기술 설명을 목적으로 출장을 갔습니다. 지금 봐도 너무 사진이 잘 나와서 마음에 너무 듭니다. 🙂🙂)

창업, 그리고 여러 프로젝트를 통해 얻은 가치관

"나만 잘하면 된다. 다른 사람들이 못해도, 내가 이끌어가면 된다."
제가 창업과 프로젝트를 하기 전 가지고 있던 생각이었습니다. 이 생각은 얼핏 맞는 것처럼 보였습니다. 모든 역할을 제가 도맡아 처리하며 빠른 성과를 내어, 그 영광은 다 함께 받는 것이 맞다고 생각이 되었습니다. 이러한 자기 희생적인 것 처럼 보이는 저의 가치관은, 지금 보면 '돋보이고 싶다'라는 저의 욕심과 이기심이 둔갑한 것이 아닐까 생각이 됩니다.

하지만 여러 프로젝트를 하게 되며 저의 이러한 가치관은 조금씩 무너져가기 시작했습니다. 특히 창업을 하게 되면서 저는 협업의 중요성을 점점 깨닫게 되었습니다.

나만 잘한다고, 나만 재밌다고 되는 것이 아니구나!

나 혼자 한 걸음 더 나아가는 것이 아니라, 모두가 다 함께 한 걸음을 나아가는 것.

다시 모헤이, 창업을 했던 시기로 되감기를 해봅니다.

팀 위라클 (모헤이)은 저에게 이전에 상상하지 못했던 시너지를 경험하도록 해주었습니다. 기능 하나를 구현할 때도, 디자이너가 제안한 화면 흐름이 버그를 줄여주고, 마케터의 사용자 조사가 API 설계를 간소화해 주었죠. 동시에 CEO는 회사를 운영하며 저를 포함한 각 팀원이 일에 집중할 수 있도록 도와주었고, 항상 어디에선가 자금을 물어왔습니다. (ㅋㅋ)

저 역시도 CTO이자 개발 리드로서 개발팀이 유기적으로 함께 성장하는 팀이 되도록 노력을 하였습니다. 예를 들어, 새로 도입할 기술에 대해서 오전 스크럼 세션을 통해 팀원들을 가르치거나, 업무 외 시간에 따로 개발을 가르쳐주는 식으로 말이죠. 덕분에 라이브러리 업데이트나 새로운 언어 도입도 빠르게 진행할 수 있었고, 팀 전체가 한 단계씩 탄탄해지는 계기가 되었습니다.

‘내 코드가 아니면 안 된다’고 생각했던 저는, 각 분야의 아이디어와 재능이 모여야 비로소 깔끔하고 탄탄한 결과물이 만들어진다는 사실을 깨달았습니다. 혼자 달리는 속도보다, 모두 함께 페이스를 맞추며 앞으로 나아가는 힘이 진짜 경쟁력임을 온몸으로 느꼈던 순간이었습니다.

이 자리를 빌려 창업 팀 멤버였던 Luna, Anthony, Caroline, Jin에게 감사를. Mohey Forever!!

결국, 협업이란 혼자서 한 번에 한 걸음 내딛는 것이 아니라, 모두가 각각의 강점을 살려 동시에 한 발씩 앞으로 나아가는 과정이었습니다. 함께 머리를 맞대고 기똥찬 아이디어를 주고받을수록 서비스가 성장하는 것을 경험했고, 각자의 작은 성공이 모여 큰 결과를 만들어내는 것을 보았습니다.

협업은 마치 음악 밴드와 같다고 생각을 합니다. 제가 아무리 피아노 솔로를 멋지게 연주해도, 드럼이 엇박을 치거나 베이스가 리듬을 놓치면 공연은 엉망이 되거든요. 🎹🎹

새내기때 어벤져스 (Avengers) 영화를 재미있게 봤었던 기억이 납니다. 저와 제 친구들은 단 한 명의 인물만을 '히어로'라고 부르지 않았습니다.
모든 인물들은 각자의 서사와 역할이 있었고, 그들 모두는 각자의 자리에서 최선을 다한 영웅들이었고, 그렇기에 우리는 그들을 한 명의 슈퍼히어로가 아닌 여러명의 어벤져스라고 부르는 이유겠지요.
스타트업을 창업하고 다양한 분야에서 일했던 팀원들과 협업을 하며 깨닫게 된 간단하지만 정말 중요한 사실입니다.

협업의 중요성을 깨달은 저는, "혼자서 슈퍼맨 코스프레하기" 대신 ‘어벤져스 닉 퓨리 모드’로 전환했습니다.
프로젝트 리더로서 진짜 임무는 혼자 모든 것을 해결하는 것이 아니라, 팀원 모두가 각자의 재주를 살려 함께 승리를 거두도록 조율하는 일이었죠.

HOLME든 SHIELDBID든, 저는 더 이상 “내 코드 아니면 안 돼!”라는 불가능 모드를 고집하지 않았습니다.
대신 실력이 모자란 동료에게는 1:1 과외로,
도입해야하는 기술에 대해서는 회의 시간을 활용한 클래스로 설명을 하며 프로젝트를 이끌었습니다.
당장은 속도가 더딜지 몰라도, 결국엔 이 길이 가장 확실하고 모두에게 도움을 줄 수 있는 방향이라는 것을 알았기 때문이죠.

이에 대한 결과로 결과로 HOLME 프로젝트에서는 프로젝트 주관 회사인 LG, SKT 양사 모두에서 최우수상을 동시에 수상하는 경험을 했습니다. 그 순간, 팀원들의 환호와 웃음을 보며 지금까지 느끼지 못했던 보람을 느꼈던 기억이 나네요. 😎😎


7. 그리고, 지금.


(GPT가 그린 저라네요...히히)

'협업'과 '팀'을 중요시하는 개발자, 나 혼자의 성장이 아닌 모두의 성장을 중요시 여기는 개발자. 취업 준비를 하며, 지금까지 해왔던 모든 프로젝트들이 저에게 양분이 되어 '개발자로서의 나'를 만들어주었다는 것을 알게 되었습니다.

회고 글을 적는 지금은, 취업 준비를 함과 동시에 Flowery라는 프로젝트를 하고 있습니다. 개발자에게 프로젝트가 얼마나 중요한지 안 저이기에, "개발자를 꿈꾸지만 프로젝트의 기회가 없었던 대학생들"을 대상으로 프로젝트 운영을 하고 있습니다. 이 프로젝트에 참여한 인원 모두가 개발자의 꿈을 키워갈 수 있다면 너무 기쁠 것 같네요. 😁😁

어느덧 한양대학교에서의 마지막 학년, 마지막 학기를 보내고 있습니다. 지금은 정말로 가고 싶은 회사의 최종 면접을 남겨두고 있고, 이 면접을 준비한다는 목적으로 학교 도서관에 앉아 길고도 긴 회고글을 적었습니다. 다 적어가는 시점에서 보니, 계속된 면접 준비로 지쳤던 저에게 힐링이 되기도 하고, '나라는 개발자는 어떤 개발자인가'를 알 수 있었던 순간이었습니다.

이제 이 길고도 긴 회고글도 끝나가고, 저라는 사람이 어떤 사람인지도 알았으니, 다시 파이팅 넘치게 취업 준비와 프로젝트 리딩을 해야겠네요. 마지막으로, 키보드 앞에서 또 한 번 심호흡을 하고 새로운 출발을 향해 달려가려 합니다.

Cheers!! 😎😎


Psalm 37:5) Commit your way to the Lord; trust in him and he will do this
시 37:5) 네 길을 여호와께 맡기고 그를 의지하라 그리하면 그가 이루시리로다
profile
운동과 음악을 사랑하는 ENFJ 개발자입니다! :)

4개의 댓글

comment-user-thumbnail
2025년 5월 11일

규민아ㅋㅋㅋ 누나랑 파이썬하던 시절이 엊그제같은데 네이버 좋은 결과 있을거라고 믿는다~ 규민이 떨구면 네이버의 손해;;

1개의 답글
comment-user-thumbnail
2025년 7월 3일

진짜... 어메이징하고 정말 정말 열심히 사셨네요... 어떻게 이게 가능한 건지 비법 좀 알려주세요..

1개의 답글