다른 부트캠프를 해본 것은 아니지만 소프트웨어 마에스트로가 다른 부트 캠프랑 다른 가장 큰 차이점은 바로 팀 매칭, 엑스퍼트 매칭, 멘토 매칭 즉 6개월 동안 함께할 팀을 전부 직접 구해야한다는 점인 거 같습니다.

 

소마는 멘티 300여명, 엑스퍼트 50여명, 멘토 120여명 이렇게 구성이 됩니다.

엑스퍼트는 앞서 소프트웨어 마에스트로 활동을 하셨던 근 기수의 선배님들로서 팀 단위가 아닌 개인 단위로 소마 활동, 개발자 커리어 등등 다양한 주제에 대해 멘토링을 제공해주시는 대학교의 조교 같은 분들이라고 생각하시면 됩니다.

 

총 구성은 멘티 3명, 멘토 3명이서 한 팀을 이루고 멘티 1명 당 1명의 엑스퍼트가 매칭이 됩니다.

앞서 말했듯이 이 모든 구성원을 직접 정해야 합니다. 

 

그럼 대체 어떤식으로 정하게 될까요? 멘티, 엑스퍼트, 멘토 각자 본인을 소개하는 자기 소개 페이지가 모여있는 노션 스페이스가 있습니다. 그걸 보면서 직접 연락을 하고 만나뵙고 이야기를 나누고 매칭을 해야합니다.

당연히 내가 원하는 분들로만 구성하기는 매우 어렵겠죠? 정말 피튀기는 현장이고 마치 연애하는 거 같기도 하고 약육 강식의 야생 같기도 하고... 정말 쉽지 않습니다. 

 

특히 저는 대구에 살기 때문에 적극적으로 연락을 하고 만나뵙기가 매우 어려웠죠.

하지만 이를 위해 구성원 모두가 모일 수 있는 워크샵 행사가 있습니다!

입구에 있던 현수막... 뽕 찬다 뽕 차!
행사가 열렸던 메인 홀... 사람이 너무 많아서 귀가 아프고 피곤했어요..ㅜㅠ

 

경기도 양평에 한 호텔에서 정말 모두가 모여서 다양한 레크레이션 활동을 하고 소마 선배님들의 특강 (아주 유익했습니다!! 최고!)밥도 먹고 이야기도 하면서 이야기를 합니다. 그 와중에 보이지 않는 탐색전... 매우 기가 빨립니다..ㅠ

 

저는 대구에서 출발해서 새벽 5시에 일어나서 아침 9시까지 도착해서 자체 버스를 타고 양평으로 이동했습니다.

매우 피곤한 상태에서 낯선 300명의 사람들과 이야기한다고 정말 힘들었습니다...ㅠㅜ 저는 이제 더 이상 이들을 만날 기회를 만들기가 정말 어렵겠다고 판단하여 최대한 많은 사람들을 만나보고 이야기 하려했습니다. 허나 대구 출신이라는 점과 학교 재학 중이라는 점 때문에 많은 분들이 저와 팀을 꾸리기 꺼려하시는 게 대놓고 표현은 안하시지만 느껴지더라구요..ㅜㅠ 너무 힘들었습니다..

 

다행히 숙소는 매우 좋았습니다. 밥도 줬는데 뭐 나쁘지 않았던 거 같습니다. 충분히 먹을 만 했어요!

 

이 많은 인원들이 전부 호텔에서 숙식을 하다니... 역시 정부 사업인지라 지원이 빵빵하네요!! 덕분에 너무 힘들었지만 편안하게 휴식했습니다. 

 

아무튼 중요한 건 바로 팀원들이죠! 저는 10명이 넘는 사람들과 계속 이야기하면서 사실 너무 지쳤습니다. 다들 워낙 대단하신 분들이라 함께 팀을 한다면 너무 좋을 거 같았지만 아무래도 지방에 거주해서 자주 대면하기 힘든 상황을 설명하다보니 선뜻 같이 팀을 하자고 결론이 나는 분이 없었습니다..... 그러다가 너무 지쳐서 잠시 구석에 앉아서 멍때리고 있었습니다 ㅋㅋ 

 

그러다가 소마 17기 전체 카톡 방에서 멘토님들이 저희를 찾는다는 소식을 듣고..ㅋㅋㅋ

물론 저를 찾으셨는지는 모르겠지만.. ㅋㅋㅋ

잠시 이쪽으로 가서 이제 말 좀 그만하고 남들이 하는 말이나 듣고 있어야 겠다... 라고 생각했습니다. 

어슬렁 어슬렁 가가지고 멘토님들이 하시는 말씀도 듣고 질문도 하고.. 팀 매칭에 어려움을 토로하고 위로도 받았습니다 ㅋㅋㅋ

그러다가 한 멘토님이 제게 해주신 말씀...

 

"너 지금 여기서 이러고 있을 때가 아니야 지금 여기서 못 찾으면 앞으로도 찾기 힘들다! 너하고 비슷한 처지에 있는 사람들을 찾아!! 분명히 있을거다!"

 

이 말을 듣고 정신이 확 들어서 바로 다시 저와 비슷한 사람들을 찾으러 떠났습니다! 그러다가 건물 입구에서 두 분을 만났고 분위기도 정말 좋았습니다. 그 분들은 이미 팀을 결성하기로 하였고 한 분은 안드로이드 한 분은 굉장히 특이하게 게임 개발 경험도 있고 백엔드 개발을 하시는 분 이였습니다. 제 입장에서 이 분들과 팀을 한다면 정말 좋을 거 같았습니다.

구하기 정말 힘든 프론트도 있고 또 저는 이번에 백엔드쪽 작업도 하지만 프로덕트 레벨의 생성형 AI 및 Agent 쪽을 제대로 해보고 싶어서 다른 백엔드 가 있더라도 전혀 상관이 없었습니다.

 

하지만 그 두 분은 서울과 가까운 경기도 지역 (용인, 오산) 에 거주하고 계셔서 아.. 이번에도 역시 안되겠지? 라고 생각했고 그 와중에 다른 백엔드 분이 오셔서 두 분에게 자기랑 같이하자고 설득을 하시길래 정말 기가 빨렸습니다.. 이게 무슨 워크샵인지 솔로지옥인지..ㅜㅠ 그래서 다 포기하고 들어가서 잤어요...

 

그렇게 워크샵이 끝날 때 까지 팀원이 될 사람들을 구하지 못해서 너무 막막했습니다. 어떻게 팀원을 구해야할 지 눈 앞이 깜깜해졌죠.. 그러던 와중 갑자기 카톡이 왔습니다!

워크샵 끝나고 이틀 뒤에 온 카톡!

많은 사람들을 거쳐 지푸라기라도 잡는 심정으로 가서 만났던 마지막 두 분에게서 연락이 온 겁니다!! 저는 당연히 바로 Ok 했습니다. 그렇게 팀 구성이 끝나게 됩니다.

 

알고보니까 한 분은 저보다 3살 많으시고 한 분은 저보다 3살 어리시더라구요. ㅋㅋㅋ 그래서 바로 팀명이 떠올라 버렸습니다.

3 6 9 !!

 3명이 각각 3살 터울이기 때문에 369라는 팀 명이 떠올랐고 팀 모두가 아 이거다!! 라고 생각했습니다. 그리고 저희 목표는!

"AI 로 사람들에게 지속 가능한 가치를 줄 수 있는 모바일 앱 서비스를 만들자!"

였습니다. 다행히 소마 활동에 관련한 가치관에 대해 3명이 잘 맞아떨어지더라구요. 저희 3명은 취업이고 창업이고 일단 좋은 서비스를 만들고 싶었습니다. 서울에 살고 지방에 살고가 중요한 게 아니라 가치관이 잘 맞는게 중요한 것 같습니다!

 

막막했던 팀 매칭이 끝나 안도하던 것도 잠시 이제 엑스퍼트와 멘토를 찾아야 했습니다.

저는 서버 개발자로 일하고 싶기 때문에 서버 개발 쪽으로 근무하시고 계시면 좋겠다고 생각했고 이왕이면 제가 IT 대기업에 가고 싶기 때문에 IT 대기업에 근무하고 계신 분이면 더욱 좋겠다 생각을 했습니다.

 

그래서 엑스퍼트 명단을 쭉 훑어보면서 연락할 분들을 뽑아봤습니다. 전부 하나 같이 너무 대단하신 분들이셔서 너무 행복했습니다. 실제로 연락을 하기 전까지는요..ㅋㅋㅋ 실제로 연락을 하니 다들 이미 빠르게 엑스퍼트님들을 선점하셨더라구요. 한 3~4번은 까인 것 같습니다.ㅜㅠㅜ 

아무튼 저는 현재 IT 대기업인 L사에 서버 엔지니어로 근무하고 계시는 엑스퍼트님을 제 담당 엑스퍼트 님으로 모시게 되었습니다. 물론 선착순으로 받으시고 계셔가지고 쉽게 매칭이 되었습니다. ㅋㅋㅋ

엑스퍼트 매칭은 생각보다 빠른 템포로 진행되니 유의하시길 바랍니다.

 

이 다음은 이제 저희 369팀을 이끌어주실 멘토님들을 찾아야 했습니다.

저희 팀은 안드로이드 네이티브 개발자 1명, 인프라 중심 백엔드 개발자 1명, AI 중심 백엔드 개발자 1명으로 구성되어 있습니다.

그래서 그에 맞게 

1. 안드로이드 및 모바일 쪽 멘토님

2. 인프라 + 서버 쪽 멘토님

3. AI 쪽 or 서비스 기획, 운영 쪽 멘토님

 

가장 먼저 모시게 된 분은 현재 싱가포르 쪽 핀테크 기업에서 근무하시는 모바일 쪽 멘토님이셨습니다. 소마에서 멘토로 활동하신 경력이 굉장히 오래 있으셨습니다. 소마는 멘토님들이 하고 싶다고 연장할 수 있는 게 아니라 이전 기수에서의 활약을 통해서 주기적으로 재심사를 통해서 재선임되십니다. 그렇기에 오래하셨다는 건 그만큼 실력이 있다는 방증인거죠.

 

그 다음으로 모시려고 했던 분은 서버 쪽 멘토님이셨습니다.

그런데 서버 쪽 멘토님은 매우 모시기 힘들더라구요..ㅠ 아무래도 서버 쪽으로 공부하는 멘티 분들이 많아서 그런 지 서버 쪽 멘토님 께 한 4번 정도는 차인 것 같습니다..ㅜㅠ 그러다 결국 매칭 종료 3일 전에 기적같이 현재 N사에서 서버 엔지니어로 근무하시는 멘토님에게 연락이 오셔서 겨우 매칭이 되었습니다!!

 

 마지막 한 분은 AI 쪽을 할까? 서비스 기획 쪽을 할까? 고민이 많았습니다.

저희는 AI 를 활용한 서비스를 만들고 싶은데 AI 쪽으로 프로덕트를 구현해본 경험은 없었기 때문에 AI 쪽 멘토님은 거의 필수라고 생각했었습니다. 그런데 실제 서비스를 출시하고 운영해본 경험도 없기 때문에 오히려 AI 보다 서비스 운영 및 기획 쪽 멘토님이 더 중요할 수도 있겠다고 생각했습니다. 둘 중에 하나를 고르기 참 애매했습니다. 

그러던 와중 엑스퍼트님의 추천으로 한 멘토님을 만나뵙게 되었고 다행히 이 분은 모든 팀을 선착순으로 받고 계셨습니다.

그런데 이 분의 스펙이 S사에서 근무하신 적도 있으시고 B2B, B2C, 금융 그리고 현재는 AX 스타트업 CEO 로 근무하고 계신 분이며 AI 쪽으로는 깊게 연구도 하셨고 서비스 기획 및 운영 쪽에서 수 많은 실패와 성공을 경험하신 분이기 때문에 저희가 필요한 부분을 너무나도 완벽히 채워주실 분을 찾게 되었습니다! 

 

그렇게 369팀이 결성되게 되었구요!

이제 열심히 달리는 일만 남았습니다! 다음은 저희 팀의 아이디어가 어떻게 결정되었는 지에 대해 적어보겠습니다!

 

 

'SW 마에스트로' 카테고리의 다른 글

AI SW 마에스트로 17기 최종 합격 후기  (0) 2026.04.30

2026년 AI SW 마에스트로 17기에 최종 합격하기까지 다른 분들이 쓰신 합격 후기에 너무 큰 도움을 많이 받아서 

아무도 원하지는 않지만 그에 보답하고자 이렇게 합격 후기를 씁니다!

 

일단 17기 같은 경우 총 450명을 뽑게 됩니다. 서울 센터 300명 부산 센터 150명, 총 450명을 선발했습니다. 예년보다 거의 3~4배 더 많은 인원을 선발하는지라 아마 합격 컷이 전반적으로 내려갔을겁니다. 

 

SW 마에스트로는 다음과 같은 절차로 진행됩니다.

1. 서류 전형

2. 1차 코딩테스트 전형

3. 2차 코딩테스트 전형

4. 심층 면접 전형

 

이렇게 총 4가지 전형이 진행됩니다. 선발 절차 자체는 거의 웬만한 기업 공채 전형이라고 볼 수도 있을 정도이지만 그보다 훨씬 많은 인원을 선발하기 때문에 충분히 도전할만하죠. 저는 곧 본격적인 취업 준비 생활을 앞두고 가볍게 실전 코딩테스트 및 면접에 대한 경험을 쌓기 위해 지원했습니다. 워낙 명성이 높은지라 합격할 거란 기대는 당연히 안했었습니다.

 

서류 전형

 성의 있게 작성한다고 했을 때 사실 서류 전형에서 떨어질 경우는 거의 없다고 봅니다. 질문은 다음과 같습니다.

 

[자기소개] AI 및 SW분야의 전문성을 키우기 위해 몰입했던 경험과 도전이 무엇인지, 또한 이러한 성장과정을 통해 얻은 배움은 무엇인지를 서술하여 주시기 바랍니다. (최소 400자, 최대 1000자 입력가능)

 

[연수계획서] AI·SW마에스트로 과정 참여를 통해 어떠한 프로젝트를 수행하고 싶은가요? 해당 프로젝트를 수행하기 위한 계획과 이루고자 하는 목표가 무엇인지 구체적으로 서술하여 주시기 바랍니다. (최소 400자, 최대 1000자 입력가능)

 

사실 여기서 작성한 내용은 이후 면접 과정에서도 언급되지 않을 가능성이 큽니다. (직접 언급하지 않는 한)

그렇지만 서류 전형을 연습해보기 위해서 열심히 썼었던 기억이 있네요..ㅎㅎ

 

1차 코딩테스트 전형

알고리즘 문제 4개, SQL 문제 1개가 나옵니다. 소마 코딩테스트는 SQL 문제가 있다는 점이 특이한데 제 백준 티어는 골드3 정도로 알고리즘에는 자신이 없었기 때문에 SQL을 푸는 게 가장 중요한 포인트라고 생각하여 SQL 을 특히 신경쓰며 준비했습니다.

 

준비 과정은 다음과 같습니다. 알고리즘은 평소에도 연습을 하고 있었고 감각만 살리자 싶어서 일주일 전 부터 프로그래머스에서 고득점 Kit 에 기초적인 것들을 풀었습니다. SQL 같은 경우 저는 프로그래머스에서 SQL 고득점 Kit 에서 join 빼고 풀었습니다. 솔직히 다 풀기에는 비효율적이라고 생각해서 lv3 이상의 문제들만 풀었습니다. 

 

알고리즘

문제는 4개였고 개인적으로 실버 ~ 골드 하위 정도 인것 같습니다. 전부 어떤 알고리즘을 쓴다기 보다 큐, 스택 등 적절한 자료구조를 선택하고 문제의 요구 사항을 잘 반영하는 것이 중요한 구현 문제였습니다. 오히려 너무 복잡한 알고리즘을 생각하시면 방해가 될 거 같았습니다. 평소에 준비를 착실히 해두셨다면 충분히 푸실 수 있을 겁니다.

 

SQL

문제는 1개였고 Union 을 적절히 잘 사용했다면 무난히 푸실 수 있는 문제였습니다.

 

저는 알고리즘 1, 2, 3번 문제와 SQL 문제를 풀었고 총 4솔이였습니다. 시간이 있었다면 아마 4번 문제도 무난히 풀었을 겁니다. 여기서 벽을 느끼신 분들도 여럿 있는 것으로 보이던데 저는 삼성 기출 문제를 풀길 추천드립니다. 몇 개만 풀어보시면 웬만한 구현 문제는 엄청 쉽게 느껴집니다.

 

매년 예상 컷은 2~3솔 정도인 것 같습니다. 3솔을 목표로 열심히 준비하시길 바랍니다. 허나 이번 기수에는 그보다는 낮은 2솔 정도인 것으로 보이더라구요. SQL 문제를 풀어낸다면 알고리즘에서 가장 쉬운 문제만 풀면 되는 거죠.

 

1차 코딩 테스트를 통과하고나면 3배수가 남고 저희 때는 900명이 남았습니다. 

 

2차 코딩테스트 전형

사실 2차 코테가 가장 어렵고 중요한 과정인 것 같습니다. 여길 통과하면 1.5배수를 남기기 때문에 확률이 꽤 많이 올라가죠. 그만큼 난이도도 어려운 것 같습니다. 마찬가지로 저는 알고리즘에는 자신이 없었기 때문에 SQL을 정말 열심히 준비했습니다.

 

준비 과정은 다음과 같습니다. 알고리즘은 프로그래머스의 알고리즘 고득점 Kit 에서 DFS/BFS, DP, 그리디 문제들을 풀어보았습니다. SQL은 프로그래머스의 SQL 고득점 Kit 에서 Lv3 이상의 모든 문제들을 풀고 Rank 등 다양한 윈도우 함수를 사용하는 문법을 익혔습니다. 여기서 어느 정도의 난이도로 나오는 지 감이 잘 안잡히실텐데 저는 left join, join, group by, ifnull 정도만 잘 사용하신다면 문제 없을거라고 생각합니다.

 

알고리즘

마찬가지로 문제는 4개였고 개인적으로 골드 중상위 정도 인것 같습니다. 하나하나 복기를 해보면 다음과 같습니다.

 

1번 : 꽤 복잡한 구현문제 였습니다. 이 문제는 시간복잡도 함정이 있어서 그 부분까지 생각했어야하는 문제였습니다. (저는 그걸 해결 못했습니다.. O(N^4) 정도가 나오면 웬만하면 잘못된 경우더라구요..)

 

2번 : 이 문제는 2차원 DP 문제였습니다. 풀이를 한번에 떠올리기 어려울 수는 있지만 침착하게 생각해보면 충분히 점화식을 떠올릴 수 있는 문제였습니다.

 

3번 : 이 문제는 Priority Queue 를 사용하는 문제였습니다. 사실 이 문제는 결국 못 풀었기 때문에 저도 잘 모르겠습니다..ㅠ

 

4번 : 이 문제는 누적합 문제였습니다. 최소경로를 찾는 문제로 보여서 BFS 인가 싶었는데 결국 BFS + 누적합 문제더라구요.. 손도 못댔습니다..ㅠ

 

SQL

마찬가지로 문제는 1개였고 정말 복잡한 수식을 도출해내는 문제였습니다. DENSE_RANK 도 나왔구요.. 정말 어렵다는 이야기가 많았고 저도 30분 동안 헤멨습니다. 풀고 나니 가장 중요한 것은 복잡한 문제의 요구사항을 단계를 적절히 나눠서 쿼리를 하나하나 작성하고 이들을 잘 join 하여 조립하는 것이 가장 중요한 것 같습니다. 어려워보여도 차근차근 쿼리 하나하나 검증해가면서 해나가시면 실마리가 보일겁니다. union 도 굉장히 유용하게 쓴 거 같아요.

 

결국 저는 2.5솔 (알고리즘 1.5솔, SQL 1솔)을 했구요. 작년 커트라인도 2.5솔 정도였던 것으로 알고 있습니다. 그리고 이번에는 대략 1.5솔 정도라고 하더라구요. 이번에는 정말 할 만했죠. 하지만 이걸 보는 여러분들은 적어도 3솔은 목표로 하시길 바랍니다.

 

4. 심층 면접 전형

2차 코테 합격 소식을 받고 기쁨은 잠시 노션으로 포트폴리오를 작성해서 제출해야했습니다. 마침 저는 제 포트폴리오를 작성하고 있었어서 노션에 맞춰서 조금 수정한 다음 바로 제출했습니다.

소프트웨어 마에스트로 센터인 마포구의 포스트타워

저는 대구에 거주하고 있고 면접 시간이 금요일 9시라서 당일날 아침 올라가기는 매우 힘들어보였습니다.. 그래서 숙소를 잡고 전날 미리 올라가야겠다고 생각했습니다. 마포구에 도착하니 마음이 벅차더라구요. 전날 올라가서 면접을 보게 될 포스트 타워를 구경하고 근처 투썸플레이스에서 하루종일 면접을 준비했습니다.

면접 준비는 오픈 카톡방에 소마 준비방이 있고 거기서 예상 질문들이 올라와 있었습니다. 그거 위주로 준비한 거 같은데 사실 제대로 써먹은 거는 지원동기? 정도였던 거 같습니다.

차디찬 계단에 앉아있는 나..

전날에 엄청 일찍 도착했습니다. 한 8시 10분?? 근데 9시까지 들어가지 말라는데 근데 딱히 있을 곳이 없었습니다. 그래서 계단에 웅크려 있었습니다. 다른 층은 전부 우체국 층이라서 다른 데도 갈 수가 없었어요...ㅠ 너무 일찍 가지 마세요..

저는 5분과 2번을 부여받고 떨리는 마음으로 순번을 기다리고 있었습니다... ㄷㄷㄷ

실제로 면접 때 받았던 질문은 아까도 말했지만 미리 준비한 질문은 거의 없었습니다. 

면접관 5분에 면접자 5명이였습니다. 공통 질문을 한 5개 정도 진행하고 그 다음 개인당 2~3개 정도의 질문을 했던 걸로 기억합니다. 정확히 기억 안나지만 이 정도 였던 것 같습니다.

 

공통질문

1. 자기소개, 지원하게 된 동기, 얼마나 많은 시간을 쏟을 수 있는 지

2. 아래 3개 중에 하나 선택해서 답변

  2.1. 멘토님에게 멘토링을 받다가 서로 의견이 충돌하는 상황에는 어떻게 할 것인지?

  2.2. 멘토님이랑 매칭이 되어서 저녁식사를 같이 먹을 때 어떤 질문을 할 것인지?

  2.3. 개발 진행 상황이 30퍼센트 진행된 상황에서 기한이 절반 밖에 남지 않았을 때 어떻게 대처할 것인지?

3. 본인이 팀 프로젝트를 진행할 때 합의해야하는 것 중 가장 중요하게 생각하는 거 하나 그리고 그 이유 (코드 포맷, 그라운드 룰 등)

4. 본인이 풀었던 코딩테스트 문제 복기했는지? 아쉬운 게 있었다면 어떤 부분이였는지?

5. 요즘 보안 이슈가 많이 벌어지는 데 본인이라면 어떻게 설계할 것 인지?

 

개인질문

제가 작성한 포트폴리오 기반으로 질문하셨습니다.

https://transparent-mind-43e.notion.site/Lee-DongGeon-efa6c3a586c28331b3fe815bd4d48845?source=copy_link

 

이동건 (Lee DongGeon) | Notion

세상과 시스템 속 다양한 문제들을 직접 정의하고 해결하며 성과로 증명해온 소프트웨어 엔지니어

transparent-mind-43e.notion.site

1. 실패했던 RAG LLM 어시스턴트 프로젝트에 대해 인상이 깊었는데 이거를 다시 한다면 어떤 방식으로 개선해볼 것인지?

2. 인턴 경험을 통해서 scale-out 을 경험했는데 왜 scale-out 을 했어야만 했나? 판단 근거는?

3. 포폴을 보니 여러 IT 동아리에서 운영진, 대표 경험이 많은 것 같은데 이 과정에서 배운 것이 있는지?

 

솔직히 저는 면접을 준비해봤던 적이 없어서 아쉬움이 많이 남았던 것 같습니다. 

전반적인 제 느낌은 기술적인 거는 포폴에 작성된 거 위주로 여쭤보셨던 거 같고 결국 당신은 소마에서 6개월이라는 긴 시간 동안 프로젝트를 잘 수행할 수 있는 사람인가? 를 궁금해 하시는 것 같았습니다. 면접을 보는 5명중에 3명이 뽑히는 꼴이죠. 그렇기 때문에 너무 긴장하실 필요없이 자신감 있게 이제껏 해왔던 경험을 잘 풀어내신다면 합격하실 수 있을 겁니다.

기본적인 지원동기, 갈등해결 같은 부분들도 처음 준비해봤는데 저와 비슷하게 면접 경험이 없으시다면 그런 부분들은 꼭 준비하고 많이 연습하고 가세요!! 

 

최종 결과 발표

금요일날 면접을 보고 다음주 수요일날 결과가 나왔습니다.

꺄호오오우~!

무려 4차에 이르는 정말 긴 과정동안 저 멀리 서울까지 가가지고..ㅠ 정말 힘들었던 것 같습니다. 그만큼 정말 기뻤습니다. 대학교 2학년때 소마를 하셨던 선배님이 그렇게 멋있어보였었는데... 시간이 흘러 저도 하게되었네요! 여러분들도 하실 수 있습니다! 코테 열심히 준비하세요! 특히 SQL!

 

지금은 이제 자기소개 노션 페이지를 작성하고 있는데 합격한 다른 인원들을 보니 정말 다들 대단합니다... 기대가 되네요.

올 한해 AI SW 마에스트로에서 정말 많은 것들을 얻어가고 싶습니다... 이 글을 보는 예비 18기 분들께도 제 기운을 나눠드리겠습니다!! 화이팅!!

 

'SW 마에스트로' 카테고리의 다른 글

AI SW 마에스트로 17기 369팀의 탄생  (0) 2026.05.20

도커는 운영체제 가상화를 지원하는 소프트웨어이다.

가상화 란 단일한 물리 하드웨어 시스템을 여러 환경으로 나눠 각각 운영체제와 애플리케이션을 실행할 수 있도록 하는 것이다. 가상화에는 크게 3가지 방식이 있는데,

1. 호스트 가상화

2. 하이퍼바이저 가상화

3. 컨테이너 가상화

1번 호스트 가상화는 호스트 OS 위에 게스트 OS 가 작동하는 방식으로, 가상화 소프트웨어가 이를 지원한다.

2번 하이퍼바이저 가상화는 하이퍼바이저(Hypervisor)라는 프로그램 위에 별도의 OS들이 작동하는 방식이다. 흔히 가상머신(VM) 방식이라고 한다.

 

위 두 방식은 각 가상환경마다 OS 를 구동시킨다는 특징이 있다.

이러한 방식은 안정적으로 가상화 환경을 구성할 수 있다는 장점이 있어 AWS 등 유명 클라우드 컴퓨팅 서비스에서 사용하고 있다. 그러나 이는 높은 사양을 필요로 하여 구성할 수 있는 가상환경에 제한이 있으며 프로비저닝(특정 환경이 실행되기까지 필요한 사전준비 과정) 에 많은 시간이 걸려 가상환경을 이동하는 것에 제한이 있다.

특히 MSA 등 서버 구조가 자주 바뀌는 상황에서 이러한 방식을 사용하는 것은 충분히 비효율적이며 그래서 등장한 것이 컨테이너 가상화이다.

3번 컨테이너 가상화는 단일 OS 위에 관리 SW가 논리적으로 컨테이너를 나누어 사용하는 방식이다.

컨테이너애플리케이션이 독립적으로 실행될 수 있는 환경을 의미하며 각각의 컨테이너는 동일한 OS 위에서 동작하지만, 각 애플리케이션의 독립된 실행을 위하여 Linux kernel의 기능을 사용하여 프로세스를 분리해서 각 애플리케이션이 철저히 격리된 환경에서 실행된다. 

 

일단 기존 가상화 방식(VM)에 비해 컨테이너는 사용하는 메모리가 적고, 가동과 중단이 빨라 효율적이다.

또 새로운 소프트웨어가 개발되었을 때 이를 적용하여 배포하기 쉽고 빠르다. 도커는 애플리케이션의 실행을 위한 정보들을 캡슐화하여 저장하고, 이를 이미지(docker image) 라는 하나의 패키지로 만들어 공유할 수 있어서 

이미지를 통해 컨테이너를 구성하여 실제로 애플리케이션을 실행할 환경을 제공한다.

그리하여 느슨하게 연결된 다수의 구성요소로 애플리케이션을 구성하는 MSA 방식에도 적합하다고 할 수 있다.

 

물론 보안문제나, 가상머신(VM) 만큼 독립된 환경을 제공하지는 않는다는 문제가 있지만,

중요한 건 나에게 필요한가? 이다. 백엔드 개발을 공부하는 입장에서, 해커톤 등 프로젝트를 진행하면서, 도커를 사용해본 결과 느끼게된 장점을 생각해보면

 

1. 굉장히 간편하다.

 사실 이게 가장 큰 장점인 것 같다. 아무래도 백엔드 개발이라면 단순 정적파일(html, image 등)이 아닌 동적 애플리케이션을 배포해야하기에 복잡한 프로비저닝 과정이 필요하다.

java 애플리케이션을 배포하는 경우 도커를 사용하지 않는 다면 과정이 굉장히 복잡하다. java를 버전에 맞게 설치하고... github에서 레포지토리의 코드를 불러오고... 빌드 툴을 통해 실행파일을 만들고.... 그 실행파일을 실행시키고...

그 과정에서 정말 다양한 오류가 나왔던 것 같다.

또 DB를 운영하기 위해 서버에 DB를 설치하고... 사용자를 생성하고... 스키마를 구축하고... 필요한 요소가 있으면 계속해서 새로 설치하고 테스트하는 과정을 반복해야한다.

도커를 사용하면 그럴 필요 없이, 로컬 환경에서 바로 java 애플리케이션 실행파일을 이미지로 저장하고, DockerHub 를 통해 공유한 다음, 서버에 바로 이미지를 내려받아 컨테이너로 실행시키면 끝이다. DB 또한 이미 만들어진 DB 이미지가 있다. mysql, postgre 등등... 그대로 받아와서 컨테이너로 실행을 시켜, 두 컨테이너를 잘 연결시켜주면 끝이다.

 

2. 이미 잘 만들어진 다른 서버를 구축하기 쉽다.

 도커의 이미지들은 도커 허브(docker hub)라는 원격 저장소를 통해 공유되는데 그렇기 때문에 남이 잘 만들어놓은 이미지들을 필요에 따라 별도의 준비과정 필요없이 고대로 갖다 쓸 수 있기에 능률이 확실히 오른다.

 

3. CI/CD 파이프라인을 구축하기 용이하다.

CI (Continuous Integration)는 지속적 통합을 의미하며, 소프트웨어의 변동사항이 지속적으로 반영되어 통합되는 것을 의미한다.

CD(Continuous Deployment)는 지속적 배포를 의미하며, 소프트웨어에 변동사항이 생겼을 때 이를 지속적으로 반영하여 서비스 환경을 중단하지 않고 바로 배포하는 것을 의미한다.

대표적인 CI/CD 툴로는 gitHubAction, Jenkins 등이 있는데, 코드 저장소에 변동이 일어날 때마다 그걸 반영하여 도커 이미지를 만들고, 서버에서 새롭게 만들어진 이미지를 내려받아 컨테이너로 실행하는 모든 과정을 자동화 해준다.

이 도구들은 도커를 필수로 사용하기 때문에 마찬가지로 큰 장점이다.

실제로 프로젝트를 진행하다보면 이제 더 이상 수정할 게 없겠지? 싶어도 변동사항이 계속해서 생기기 마련이다. 심지어 운영 과정에서도 변동사항은 꾸준히 생긴다. 그럴 때 CI/CD 파이프라인의 중요성을 몸소 느꼈고, 도커는 서버 개발자라면 꼭 한 번쯤 사용해 봐야하지 않나 싶다.

 

 

Reference

https://www.samsungsds.com/kr/insights/docker_container.html

'인프라 > Docker' 카테고리의 다른 글

EC2 인스턴스에 Docker 설치하기  (0) 2024.07.22
SpringBoot 프로젝트 Docker 로 EC2에 배포하기  (0) 2024.07.22

AWS EC2에 Docker 를 설치하는 과정을 기록하고자 한다.

환경은

Ubuntu Server 24.04 LTS 기준

 

먼저 도커를 설치하는 과정이다.

sudo apt-get update

sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io

 

1. https 통신을 통해 외부 데이터 및 패키지에 접근할 수 있는 기능들을 설치한다.

2. curl 명령어를 통해 설치관리자(apt)의 identify key 값을 통해서 서버에 Docker 저장소를 설치한다.

3. 설치관리자에다 Docker Repository 경로를 추가하고

4.install 명령어를 통해 도커를 설치한다.

 

sudo 명령어는 관리자 권한을 부여하는 명령어로, putty 와 같은 프로그램으로 SSH 연결을 통해 작업을 한다면, 꼭 붙여줘야 Permisson Denied 오류가 뜨지 않기에 주의해서 꼭 붙여줘야한다.

AWS 웹 콘솔로 접속 하게되면 웬만해선 필요없을 수 있다. 그래도 Permisson Denied 가 뜨면 한번 붙여보자

 

설치한 도커를 확인하는 과정이다. 

sudo systemctl status docker

해당 명령어를 통해 설치되어있는 docker 정보를 확인할 수 있다.

 

 

도커 허브를 통해 실행하고자 하는 서버 이미지를 pull 즉 해당 환경에 불러오는 과정이다.

sudo docker search 이미지 이름

sudo docker pull 이미지 이름[:버전]

search 명령어를 통해 찾고자하는 이미지가 있는지 찾고, pull 명령어를 통해 해당 이미지를 서버에 다운받는다.

버전 같은 경우 따로 명시하지 않으면 latest 버전을 받게 된다.

 

 

불러온 이미지를 이용하여 새롭게 컨테이너를 만들어 실행하는 과정이다.

sudo docker run -p [호스트 포트]:[컨테이너 포트] -d -it --name {컨테이너 이름} {이미지 이름} sleep infinity

run 명령어를 통해 다양한 옵션을 부여하여 컨테이너를 실행시킬 수 있다.

 

-p : 포트를 바인딩해주는 옵션이다. (필수)

[외부에서 접속하는 포트] : [컨테이너 내부에서 사용할 포트] 이렇게 포트를 지정해주어야한다.

스프링 부트 서버라면, 컨테이너 내부에서 스프링 부트 애플리케이션이 주로 8080 포트로 열리고, 또 일반적으로 로 스프링부트 서버는 8080 포트를 이용하기 때문에 외부에서 접속할 포트 또한 8080 으로 해주는 것이 적절하다. 고로 스프링부트 서버를 실행시킬 예정이라면 8080:8080 옵션을 보통 부여해주는 것이 좋고 만약 안된다면 오른쪽 포트가 스프링 부트가 실행중인 포트와 정확히 일치하는지를 꼭 확인해봐야한다.

 

-d : daemon으로 실행, 즉 백그라운드(background)로 실행하도록 지정해주는 옵션이다. (필수)

스프링부트 서버는  요청이 들어올 때마다 적절한 응답을 반환해야하기에 포어그라운드(foreground)에서 사용자의 입출력과 상관없이 항상 실행되어야 하기 때문에 백그라운드 방식으로 실행되어야하기에 이 옵션을 꼭 붙여주어야한다.

 

-it : 이 옵션은 컨테이너 외부 터미널에서 해당 컨테이너에 사용자의 입력과 출력을 전달하기 위한 옵션이다.

컨테이너 내부에 접속하여 쉘 등의 CLI 도구를 통해 작업을 해야할 경우에 필요한데, 웬만하면 필요한 경우가 많으니 꼭 붙여주자.

 

-v : docker 볼륨을 지정하는 옵션이다.

--rm : 컨테이너 실행이 종료되면 컨테이너가 자동으로 삭제되도록 하는 옵션이다. 

--name : 컨테이너의 이름을 설정하도록 하는 옵션이다. (필수)

--net : docker 네트워크를 지정하는 옵션이다.

 

sleep infinity : -d 옵션을 통해 백그라운드로 실행을 했다고 하더라도, entrypoint 나 cmd가 설정되어 있지 않은 이미지 일경우 컨테이너가 바로 종료되는데, 이를 방지해줄 수 있는 옵션이다. (필수)

웬만하면 우리가 직접 개발한 스프링 프로젝트를 통해 이미지를 만들텐데, 그때 entrypoint 나 cmd 를 지정하지 않을 경우가 많을 것이기 때문에 거의 필수적으로 필요한 옵션이다.

 

그 외 기타 명령어들이다.

sudo docker ps -a

sudo docker restart [컨테이너 명]

sudo docker stop [컨테이너 명]

sudo docker start [컨테이너 명]

sudo docker rm [컨테이너 명]

sudo docker rmi [이미지 명]

 

 

ps 명령어를 통해 지금 실행중인 컨테이너를 확인할 수 있고 -a 옵션을 통해서 정지된 컨테이너 까지 확인할 수 있다.

 

restart 명령어를 통해서 실행중인 컨테이너를 중지했다가 다시 실행시킬 수 있다.

 

stop, start 명령어를 통해서 실행을 중지하거나 실행할 수 있다.

 

rm, rmi 를 통해 컨테이너와 이미지를 삭제할 수 있다.

'인프라 > Docker' 카테고리의 다른 글

Docker 란?  (0) 2024.07.25
SpringBoot 프로젝트 Docker 로 EC2에 배포하기  (0) 2024.07.22

열심히 개발한 SpringBoot 프로젝트를 Docker 를 사용하여 EC2 배포 서버에 배포해보자!

Why Docker?

docker 를 사용하는 데에는 정말 다양한 이점이 있다. 이에 관해서 포스팅한 내용이 있다.

https://himodu-tech.tistory.com/12

 

직접 개발한 스프링부트 프로젝트를 도커를 통해 배포하기 위해선 다음과 같은 과정이 필요하다.

0. 로컬 환경에 Docker 를 설치한다. (잘 나와있는 자료들이 많아서 생략)

1. 프로젝트 내에 jar 실행파일을 만든다.

2. 프로젝트 내에 Dockerfile 을 만들어 Docker 가 해당 jar 파일을 찾을 수 있도록 한다.

3. 프로젝트에서 터미널을 열어 Docker 명령어를 통해 이미지를 build 한다.

4. build 한 이미지를 Docker Hub 에 공유한다. 

5. 준비된 서버에서 Docker Hub 에 공유된 이미지를 다운(pull)받는다. (서버에는 도커가 설치되어야함)

6. 다운받은 이미지를 가지고 컨테이너를 실행한다.

 

과정이 조금 길다. 차례차례 살펴보도록 하자

 

1. 프로젝트 내에 jar 실행파일 만들기

jar 실행파일이란 java 애플리케이션 실행을 위한 모든 파일들을 압축해놓은 하나의 실행가능한 파일로써

흔히 '자르' 라고 부른다.   (교수님들도 그렇게 부르시더라... '자~ㄹ' 파일이라고 부르면 좀 어색해보인다.)

jar 실행파일을 만드는 방법은 gradle 을 사용한다면 매우 간단하다. 

먼저 인텔리제이 우측상단에 코끼리 모양  gradle 아이콘을 누른 다음 tasks -> build -> bootJar 를 선택

그럼 밑에 콘솔 창에서 실행결과를 확인할 수 있고

프로젝트 내에 build/lib 하위 경로에 실행파일이 생성된다.

 

2. 프로젝트 내 Dockerfile 생성

프로젝트 최상단에 새로운 파일을 만들고 이름을 'Dockerfile' 이라고 지어준다. 토씨 하나도 틀려선 안된다.

 (최상단이 아니어도 좋지만, 이러한 경우 build 시에 경로를 따로 명시해줘야하기 때문에 귀찮다.)

Dockerfile 의 내용은 아래와 같다.

FROM openjdk:17-jdk-alpine

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

Dockerfile 이 하는 일은 Docker 가 이미지를 빌드할 때의 정보를 제공해주는 것이다.

위에서 부터

만들 이미지의 기초가 될 java 이미지를 선택하고, (버전을 꼭 맞춰줘야한다.)

jar 파일의 위치를 매개변수로 저장하고

app.jar 라는 이름의 jar 파일로 이를 복사한 다음

'java -jar /app.jar' 라는 명령어를 해당 이미지의 ENTRY POINT 로 설정한다.

ENTRY POINT란 이 이미지를 바탕으로 컨테이너를 실행하는 그 순간 초기에 실행할 명령어를 의미한다.

 

3. 터미널을 열어 docker 명령어를 통해 이미지를 빌드한다.

docker build -t {도커허브 이름}/{이미지 이름} .

프로젝트 최상단 경로에서 터미널을 열고, 위와 같은 명령어를 입력한다.

여기서 중요한 것은!

이미지의 이름을 정해줄 때 DockerHub에서 사용하는 이름을 앞에, 해당 이미지의 이름을 뒤에 적어줘야 한다는 것이다.

해당 이미지의 이름은 얼마든지 겹칠 수 있으니 이를 식별하기 위해서 앞에 자신의 DockerHub 이름을 붙여주는것이다.

그리고 중요한 것은 마지막에 '.' 이걸 꼭 찍어주자. 이 점이 의미하는 것은 해석할 Dockerfile 이 있는 위치이다.

우리는 방금 최상단에 Dockerfile 을 만들었기 때문에 현재 위치('.')에서 찾을 수 있지만, 만약 다른 곳에 있다면 여기에 도커파일이 있는 파일경로를 적어줘야한다.

 

4. build 한 이미지 DockerHub에 업로드

Docker를 설치했다면, DockerDesktop 또한 설치되었을 것이다. GUI 로써 도커를 훨씬 편히 이용할 수 있으니 사용하는 것을 추천한다. 그리고 DockerHub 에 꼭 로그인 해주자.

하단에 'Not connected to Hub' 이라고 뜬다면

우측상단에 Sign In 버튼을 눌러 로그인할 수 있다. 소셜 로그인을 지원하기 때문에 어렵지 않을 것이다.

하여튼 성공적으로 이미지가 빌드됐다면, DockerDesktopimages/Local에 이미지가 보일 것이다.

배포할 이미지의 좌측에 세로 점 3개 버튼을 누르고 'push to Hub' 를 선택하면, Docker Hub에 업로드 되며, DockerDesktop 에 'images/Hub ' 에서 확인할 수 있다.

 

5 ~ 6 과정은

서버에 도커를 설치하고, Hub에 올라간 이미지를 다운받아 그대로 컨테이너를 실행하는 과정이고

따로 포스팅한 내용이 있다. 아래 링크를 참고하면 된다.

https://himodu-tech.tistory.com/10

 

 

아래 영상은 전 과정을 영상으로 기록한 내용이다. (영상이 안뜬다면? = https://www.youtube.com/watch?v=X6Fb6pT11eo)

'인프라 > Docker' 카테고리의 다른 글

Docker 란?  (0) 2024.07.25
EC2 인스턴스에 Docker 설치하기  (0) 2024.07.22

Spring 프로젝트를 진행하면서, 디렉토리의 구조를 정하는 데에 있어서 처음에는 딱 한가지 규칙만 생각했다.

바로 src/main/java/groupName/projectName 하위에 파일을 위치시켜야 스프링 컨테이너가 이를 인식하고 실행한다는 것

 

그래서 자연스레 아래와 같은 디렉토리 구조를 사용했다. 

계층형 구조

└── src
    ├── main
    │   ├── java
    │   │   └── groupName
    │   │       └── projectName
    │   │           └── demo
    │   │               ├── config
    │   │               ├── controller
    │   │               ├── dao
    │   │               ├── domain
    │   │               ├── exception
    │   │               └── service
    │   └── resources
    │       └── application.yml

 

이와 같은 구조는 계층형 구조라고 불린다.

- config       : configuration 파일로 이루어져있다. ex) SwaggerConfig, SecurityConfig 등등

- controller   : controller 파일로 이루어져있다.

- dao            : Jpa repository 와 구현체 들로 이루어져 있다.

- domain      : Entity, DTO 들로 이루어져 있다.

- exception   : custom exception 들과 exception handler 로 이루어져 있다.

- service       : service 들로 이루어져 있다.

 

이렇게 디렉토리 구조를 구성하면, 모든 파일을 한 눈에 보기 용이하고 구조가 단순하기에 이해하기 쉽다.

그러나 Entity의 종류가 계속해서 늘어나고 그에 맞춰 Jpa repository 가 늘어나면서, 모든 요청을 한 controller와 service 에서 다룰 수 없기 때문에 마찬가지로 늘어나면서 한 디렉토리 내에 너무 많은 종류의 controller, service 등의 쌓이게 되어

파일을 찾아내기가 곤란해졌다.

그리고 무엇보다 협업을 하다보니 이러한 불편한 점이 두드러졌다. 모두들 같은 디렉토리에서 파일을 수정하다보니, 거리낌 없이 다른 파일을 수정하게 되고, 이는 code conflict 로 이어졌다.

그러한 와중에 새로운 디렉토리 구조를 찾아보게 되었고, 마침 발견하게 된 것이 도메인형 디렉토리 구조였다.

 

도메인이라는 단어는 어떤걸까? DDD (Domain Driven Design) 와 관련이 있다.

 

DDD (Domain Driven Design)

소프트웨어 개발 방법론 중 하나로 도메인에 집중하는 소프트웨어 설계법 이다. 여기서 말하는 도메인이란, 개발자들만 필요하고 이해할 수 있는 영역이 아닌 사용자도 이해할 수 있는, 유비쿼터스(ubiquitous)하게 정의되는, 해결 해야하는 문제의 영역을 의미한다.

실제로 예를 들자면,

  학교 축제 웹페이지라면

1) 축제 부스 관련 정보를 검색 및 조회 2) 부스 페이지에 댓글 작성 및 삭제  등등...

  학교 도서관 예약 시스템이라면

1) 기존 예약 조회 및 예약 진행 2) 사용자 맞춤 기능(마이페이지, 로그인 등) 등등...

 

이렇게 해결 해야하는 문제 즉, 구현해야하는 기능들을 종류별로 나누고 그 기능들을 우선적으로 생각하면서 소프트웨어를 개발하는 방법론이다. DDD 를 지향하는 개발 방법은 여러가지가 있을 것이다.

예를 들면 ERD를 먼저 짜는 게 아니고, Controller 부터 Service 그 다음 Repository 까지 개발하는 방식이 있다. 이러한 방식은 프론트 팀원들에게 API 명세서를 빠르게 던져줄 수도 있고, 먼저 그려진 ERD에 의해 개발자의 사고가 제한되는 것을 막을 수 있다고 한다.

 

하여튼 그래서 DDD 의 주요 특징 중 하나가 Bounded Context 이다.

 

Bounded Context: A description of a boundary (typically a subsystem, or the work of a specific team) within which a particular model is defined and applicable. Every domain model lives in precisely one BC, and a BC contains precisely one domain model. BC is a specific responsibility, with explicit boundaries that separate it from other parts of the system.

https://wubw.github.io/2017/domain_driven_design_introduction/

 

간단히 말하면 앞서 말했던 도메인들의 경계를 명확히 나눠 제한된 컨텍스트로 나누어야한다는 것이다.

그래서 우리는 이걸 우리 프로젝트에 적용하고자 아래와 같은 디렉토리 구조를 도입하였다.

 

도메인형 구조

    └── src
    	├── main
    	│   ├── java
    	│   │   └── gruoupName
        │   |       └── projectName
        │   |           ├── domain
        │   |           │   ├── booth
        │   |           │   │   ├── api
        │   |           │   │   ├── application
        │   |           │   │   ├── dao
        │   |           │   │   ├── domain
        │   |           │   │   ├── dto
        │   |           │   │   └── exception
        │   |           │   ├── comment
        │   |           │   │   ├── api
        │   |           │   │   ├── application
        │   |           │   │   ├── dao
        │   |           │   │   ├── domain
        │   |           │   │   ├── dto
        │   |           │   │   └── exception
        │   |           │   └── model
        │   |           │
        │   |           ├── global
        │   |           │   ├── common
        │   |           │   ├── config
        │   |           │   ├── error
        │   |           │   └── util
        │   |           │
        │   |           └── infra
    	│   └── resources
    	│       └── application.yml

 

크게 보면 1) domain 2) global 3) infra 등으로 나눌 수 있다.

1. domain

위에서 말했던 해결해야하는 문제 즉, 도메인을 나눠놓은 디렉토리이다.

먼저 미리 구분해놓은 도메인별로 디렉토리를 만들어 준 다음 아래와 같이 구성한다.

- api              : controller 로 이루어져 있다. api를 처리하는 역할을 수행한다는 의미이다.

- application : service 로 이루어져 있다. 애플리케이션의 주요 로직을 수행한다는 의미이다.

- dao             : repositoy 와 그 구현체로 이루어져 있다. Data Access Object 라는 뜻이다.

- domain       : Entity로 이루어져 있다. 실제로 구현되는 domain 즉 data를 표현한다는 뜻이다.

- dto              : DTO Data Transfer Object 로 이루어져 있다.

- exception    : 해당 도메인에서 발생할 수 있는 예외(RuntimeException)들로 이루어져 있다.

 

직관적으로 api 가 아니고 controller 이런 식으로 정하면 안되나? 생각이 든다.

허나 위 폴더 명들은 구글링을 했을 때 가장 빈번하게 볼 수 있는 이름들이다. 폴더의 이름은 일종의 약속이다. 기술적인 요소나 다른 나에게 편한 이름을 고려하기 보다 다른 개발자들 과의 소통과 협업을 고려하여 그냥 다른 사람들이 많이 쓰는 걸 쓰는 게 맞다고 생각한다.

2. global

도메인 마다 적용되는 것이 아닌, 프로젝트 전역(global)에 적용되는 파일들을 모아놓은 디렉토리이다.

- common  : 기본적으로 모든 곳에서 사용되는 BasicResponse DTO나 모든 Entity들이 상속하는 BasicEntity 등 으로 이루어진다.

- config      : 전역적으로 적용되는 Configuration 들로 이루어진다. ex) SwaggerConfig, SecurityConfig etc

- error        : 전역적인 예외나 전역적으로 예외를 처리하는 ExceptionHandler 들로 이루어진다.

- util           : util 용 파일들로 이루어진다.

 

3. infra

infrastructure 관련된 코드들로 구성된다. infrastructure는  이메일/SMS 알림,외부 API 등 외부 서비스에 대한 파일들을 모아놓은 디렉토리이다. 

 

 

암튼 그렇다. 실제로 위와 같이 디렉토리를 적용해보니 개발 단계에서도 협업할 때 분업을 하기가 좋았다. 그저 하나씩 도메인을 정해서 디렉토리를 만들어서 그 위에서만 작업하면 되니까 github merge 할 때도 굉장히 편했던 거 같다.

그리고 리팩토링 할 때도 모든 코드를 한 번에 다 한다고 치면 진짜 토나올 거 같은데, 도메인 폴더 별로 오늘은 이거 내일은 저거 이런 식으로 하면 생각보다(?) 편한 것 같다...

 

Reference

https://velog.io/@haron/Spring-Project-Structure

 

'스프링' 카테고리의 다른 글

SpringBoot 생성자 패턴 (Lombok)  (0) 2024.07.08
SpringBoot 서버 시차 맞추기  (0) 2024.07.04

객체를 생성하는 방법은 여러가지가 있다. 생성자 패턴, 빌더 패턴 등등

어떤 방식이 가장 적절한 방식일까? 객체지향 설계에 맞는 생성자 패턴을 고민해보았다.

SpringBoot 애플리케이션 내에서는 다양한 종류의 객체들이 존재한다.

1. SpringBootApplication

2. Controller, Service, Repository

3. DTO, Model

4. Entity (Jpa)

5. 그 외 등등..

 

1번 SpringBootApplication

별도의 의존관계가 없이 Singleton 으로 동작하는 방식이고 SpringBoot Application 을 생성하면 자동으로 해결이 된다.

 

2번 Controller, Service, Repository

Controller-Service-Repository 구조안에 있는 객체들이다. 마찬가지로 Singleton 객체임을 보장한다. @Service, @Controller, @Repository 등의 어노테이션이 스프링 컨테이너가 이를 스프링 빈으로 감지하게하기 때문이다.

서로 호출하는 방향이 정해져 있다. (controller -> service -> repository) 서로 연결되어 있기에 그 생명주기를 같이한다.

(서버의 시작과 끝 스프링 컨테이너에 의해 생성, 소멸) 

controller - service 참조 예시

 그들사이 연결 즉, 의존이 완고하고, 변할 일이 잘 없다. 그렇기에 내부 의존 관계에 대해 private 지정자를 설정하여 외부에서 접근할 수 없게하고, final 을 통해서 상수로 지정해 절대 변경하지 못하도록 하여 안정성을 보장한다.  

Controller-Service-Repository 이 구조는 어떻게 보면 정말 절대적인 구조이고, 절대 변경되지 않아야하는 만큼 private 과 final 로 구조 사이 연결을 견고히 할 수 있는 것이다. final 을 통해 절대 불변의 객체라는 것을 명시하면 가독성도, 객체지향설계에도, 성능적으로도 이점이 있다! 변할 가능성이 없으니 메모리 할당을 미리 배제시킬 수 있기 때문이다.

 

@RequiredArgsConstructor 는 Lombok 프로젝트의 기능 중 하나로, 해당 어노테이션이 붙은 객체의 required argument 가 들어간 생성자를 만들어준다. 그럼 스프링 컨테이너가 

 

Required arguments are final fields and fields with constraints such as @NonNull

https://projectlombok.org/api/lombok/RequiredArgsConstructor

 

document 를 통해서도 알 수 있듯이 Required Argument란 final 이 붙어있거나, @NonNull 이 붙은 필드값을 의미하고 그것들을 담고있는 그야말로 절대불변의 필수적인 필드값을 생성하는 생성자를 만드는 것이다. 이러면 별도로 @Autowired 어노테이션이나 생성자를 만들어 줄 필요가 없다.

접근 지정자의 경우 기본적으로 public 이다. 그러나 해당 객체는 로직 내에서 생성자가 사용될 일이 없이, 스프링 컨테이너에 의해 싱글톤(singleton)객체로 관리된다. 그렇기 때문에 access 레벨을 protected 으로 설정하여 객체의 무분별한 생성을 막아줘야한다. (어 근데 private 은 안된다. 왜 그렇지...)

 

access 속성을 AccessLevel.PROTECTED 로 설정해주어 객체를 관리한다.

3번 DTO, Model

DTO 와 Model 서버 계층에서 데이터를 저장하고 처리하기 위한 객체이다. 주로 Service 내에서 Entity 의 정보를 저장하고 이를 비즈니스 로직에 맞게 가공하여 Controller에서 반환하는 역할을 수행한다.

해당 객체들은 Service 객체에 의해 생성될 것이다. singleton 도 아니다. Builder 패턴을 주로 사용한다.

 

Builder 패턴?

빌더 패턴은 자료가 워낙 많아 간단히 설명하면, 객체의 생성 시 객체 내 필드 중 원하는 필드만 쏙쏙 골라 이를 매개변수로 받아 객체를 생성하는 디자인 패턴이다. 장점은 유연하게 객체를 생성할 수 있고, 내부 필드값을 주입할 때 그 순서를 모르더라도 메서드 명으로 통해 명시적으로 어떤 필드 값을 주입하는지 알 수 있고, 순서도 알 필요가 없다는 점이다.

 

그럼 왜 DTO에 Builder 를 쓰는 걸까? DTO 는 주로 Entity의 정보들을 받아 재구성하여 비즈니스 로직의 중간단계, 서버 응답의 최종단계 등 다양한 용도로 사용된다. 필요에 따라 사용하는 데이터의 값이 Entity와 대부분 완전 일치하지 않기에 Entity 의 값들을 DTO에 옯길 때 

 

그런데 한 가지 의문이 든다. 빌더 패턴을 사용하지 않고, DTO를 생성한 다음, setter 메서드를 통해서 내부 필드 값들을 선택적으로 초기화해주면 되는거 아닐까? 논리적으로는 다를 게 없다.

허나 setter 메서드의 경우 객체가 생성되고 나서 또 다른 객체를 주입받아 내부 필드값으로 지정하는 방식이다. 그런데 이러한 방식에 대해서 객체의 불변성, 도메인 영역과 응용 영역의 구분이 모호해지는 등 다양한 문제가 있다. getter 메서드 또한 내부 참조관련해서 문제가 있다고 하니 쓰는 것을 지양해야한다고 한다. 이건 나중에 한번 다시 봐야될 듯 하다.

 

필드 값이 2개 있는 DTO에다 빌더 패턴을 직접 구현해보자!

CommentRequest 의 필드값

 

내부에 Builder 객체 구현

Builder 객체 내부에 CommentRequest 의 필드 값을 복사해준 다음, 각 필드 값마다 초기화 메서드를 만들어준다. 반환 값은 초기화 다음 Builder 자기 자신을 반환함으로써, 초기화 과정을 계속해서 이어나간다.

초기화가 끝났다면 Builder 자기 자신을 CommentRequest 의 생성자로 넘겨줌으로써 생성 과정이 끝나게된다.

Builder 패턴 의 시작과 끝

CommentRequest 내부에 builder 메서드로 생성과정을 시작하고, Builder 를 매개변수로 받는 생성자를 통해 객체를 생성하면서 끝난다.

전체적인 과정을 살펴보면 Builder 라는 객체 생성을 위해 그를 복사한 임시 객체를 만들고, 그 객체를 통해 필드 값을 초기화 한 다음 그렇게 입맛대로 초기화된 임시객체를 넘겨주어 진짜 객체를 생성한다.

아 근데 너무 귀찮다 이걸 일일이 다 구현한다고? 그래서 lombok 에서 @Builder 어노테이션을 만들어줬다.

Builder 패턴 class 상위에 넣기
Builder 패턴 생성자 상위에 넣기

 

class 의 상위에 넣어주거나, 직접 생성자를 만들어서 해당 메서드에 넣어줘도 된다. 후자의 장점은 원하는 필드 값만 Builder를 통해 초기화 시켜줄 수 있다는 점이다. 그런데 그게 큰 장점이 있는 지는 모르겠다. 코드의 길이가 길어지긴 하겠지만, 애초에 빌더 패턴의 장점이 객체의 유연한 생성인데, 모든 필드 값을 후보에 넣는 것이 그 장점을 이용하는 것이라 생각한다. 애초에 DTO 라면, Entity의 id 값 등 건드릴 필요가 없는 값들이 있는 것도 아니고 모든 값들이 필요에 의해서 정의 되었을 가능성이 크기 때문이다. 그리고 AllArgsConstructor 사용을 막을 수 있는 이점도 있는데 이 내용은 밑에서 다시 다루겠다.

* RequestBody 나 ResponseBody 에 쓰이는 DTO 객체는 무조건 @Getter 를 붙여야한다. (추후에 포스팅 예정)

 

4번 Entity (Jpa)

Entity 는 Jpa에서 지원하는 객체로 ORM을 지원할 수 있게 해주는 객체이다. 자세한 내용은 추후에 포스팅 할 예정이다.

하여튼 Jpa에서는 RDB 의 특징 중 하나인 연관관계를 지원하는데, 연관관계가 설정된 객체를 불러올 때 전략 중 하나로,

지연 로딩 (Lazy Loading) 을 지원한다.

지연로딩? 

Lazy Loading

지연로딩이란 어떤 객체를 불러왔을 때 그와 연관관계에 있는 객체를 한 번에 다 불러오는 게 아니고 프록시 객체를 만든 다음에 후에 그 객체를 실제로 참조하고자 할 때 영속성 컨텍스트가 프록시 객체를 기준으로 DB에서 해당 data를 불러와 실제 Entity 를 생성하는 데이터 로딩 방식이다.

 

암튼 그래서 이때 프록시 객체를 생성하기 위해서는 기본생성자 즉 아무 필드값도 주입받지 않는 생성자가 필요하다.

기본 생성자가 없는 경우 오류메시지

@NoArgsConstructor 어노테이션은 기본생성자를 자동으로 만들어주는 Lombok 의 기능이다.

주석 처리하니까 public 이나 protected 지정자를 가진 기본생성자가 무조건 필요하다고 한다. 영속성 컨텍스트가 프록시 객체를 생성할 때 쓰인다는 것은 알겠는데 왜 protected?로 해줘야 할까? 

entity 와 proxy 의 구조

영속성 컨텍스트가 proxy 를 생성할 때 기존의 Entity 를 상속한 객체를 생성하기 때문에 protected 를 통해서 자기 자신이나 자신을 상속한 객체만 생성자에 접근할 수 있도록 해줘야한다.

여기서도 빌더 패턴을 쓸 것이다. 근데 주의할 점이 있다. 앞서 우리는 @NoArgsConstructor 를 통해 프록시 객체의 생성자를 만들어줬다.  @Builder 어노테이션은 생성자가 없을 경우 자동으로 모든 필드가 들어간 생성자를 생성해준다.

Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself or allowed lombok to create one such as with @NoArgsConstructor. If you do have an explicit constructor, put the @Builder annotation on the constructor instead of on the class.

https://projectlombok.org/features/Builder

공식 문서에서도 나와있듯이 생성자를 명시적으로 선언하지 않은경우 @AllArgsConstructor 어노테이션을 자동으로 적용해주고, 만약에 @NoArgsConstructor 와 같이 생성자를 생성해줄 경우, @Builder 어노테이션을 직접 생성자 메서드 위에다가 달아주어야 한다.

생성자에다가 직접 Builder 를 붙여준 모습

이런 식으로 직접 생성자를 만들어 준 다음 @Builder 를 붙여주면 Entity에 안전하게 빌더패턴을 적용할 수 있다.

class 위에다가 덕지덕지 붙여버리기

물론 이렇게 class 위에다가 덕지덕지 붙여도 큰 문제는 아니겠지만 일단 보기 너무 안좋고 무엇보다 AllArgsConstructor 사용을 지양하는 트렌드가 있다고 한다. 왜냐? 모든 필드가 추가된 생성자는 매개변수의 순서를 바꿔 넣을 수 도 있기 때문이라고 하는데... 크게 납득이 되진 못한다. 허나 가독성 측면에서도 그렇고 공식 문서에서도 생성자에다 직접 Builder 를 붙여주라고 하기에 나는 앞으로 계속 그렇게 할 것 같다.

 

정리를 해보면

- Service, Controller 등은 @RequiredArgsConstructor, 접근 레벨은 protected 로 설정

- DTO 는 Builder 패턴을 적용 그냥 @Builder 어노테이션 붙여주기

- Entity 는 Jpa의 프록시 객체 생성을 위한 @NoArgsConstructor(access=AccessLevel.PROTECTED), 직접 생성자를 만든 다음 @Builder 어노테이션을 붙여주기

 

이 정도로 정리할 수 있을 것 같다.  

+ Recent posts