2019년 10월, 꿈에 그리던 회사에 합격 통보를 받았다.

개발자로서 만 4년의 경력 중 가장 치열하고 힘들었던 2019년 한해 였지만 그 동안의 경험을 통해 이룩해낸 성취가 뿌듯함을 느끼게 해주었다.


첫 직장으로 삼성전자에서 2년을, 두번째 직장으로 LINE 에 경력직으로 입사했으며, 이제는 꿈에 그리던 외국계 기업에서 세번째 커리어를 시작한다.

경력대비 많은 이직을 한 편에 속해서 지인들로부터도, 개인적으로 코드를 리뷰해주는 학생들로부터 질문을 많이 받는데, 그 때 도움이 될 수 있도록 해주었던 조언들을 정리해보았다.

이제 막 커리어를 시작하는 개발자들에게는 좋은 도움이 될 것 같아 개발자로서의 진로에 관한 지극히 개인적인 경험과 생각을 공유해본다.

절대 이 내용은 개발자에게 회사를 선택하는 기준을 제시하는 가이드북이 아니며, 단지 친한 형이 개인적인 생각 말해주는 셈 쳐줬으면 좋겠다.

 

어떤 커리어를 쌓고싶은가?

 

생각보다 어려운 질문이다.

학생 때의 나는 막연히 세계적으로 명성이 자자한 구루(GURU) 가 되고싶었지만, 뭘 어떻게 해야하는지도 몰랐고 어떤 회사에서 일하고 싶은지도 몰랐다. (글쓰고 생각해보니 그때도 구글에서 일하고 싶긴 한 것 같다...ㅎ)

그리고 불분명한 생각으로 경력을 시작하게 되면 3가지 케이스로 나뉘게 된다.

 

1. 돈을 목표로 하는 경우

2. 워라밸(Work and Life Balance)을 목표로 하게되는 경우

3. 꿈을 따라가는 경우

 

어느게 좋다고 할 수도 없고, 운좋다면 2~3가지를 동시에 만족시켜주는 기업에서 일하게 될 수도 있다. (부럽습니다)

문제는 어떤 곳에서 일하게 되더라도 본인이 행복할 수 있는가, 일에서 만족감을 얻을 수 있는가 최우선이 되어야할 것이다.

그리고 더 문제는 그 사실도 모두가 아는 뻔한 정답이다.

 

만약 본인이 만족하면서 일을 하고 계신 분이라면 시간 절약을 위해 뒤로가기를 권고드린다.

이 앞으로는 지극히 뻔한 얘기이기 때문이다.

 

아래 내용은 한창 방황하던 혹은 뭘 하고 싶은지 커리어가 뭔지도 모르고 취직을 준비하던 과거의 나와 같은 분들을 위해 쓴 경험담에 기반한 글이다.

 

 

잘 갖춰진 프로세스와 화려한 커리어를 원한다면 대기업을

 

 

대기업 개발자로 경력을 계발해보는 건, 완벽한 프로세스 위에서 잘 적응할 수 있는 인재가 되는 것과 같다.

이건 좋은 의미와 나쁜 의미를 전부 포함한다.

 

- 장점

이미 갖춰진 선진 프로세스를 익히고 사업을, 서비스를 배우는 데 있어서 대기업은 훌륭한 옵션이다.

당신이 모르는 부분이 많다면 사내 인프라만 잘 활용해도 성장할 수 있는 여지는 아주 크다.

 

또한 뛰어난 네임 밸류 는 커리어 계발에 있어서 큰 도움이 된다. 

비단 후광 효과 뿐만 아니라 책임감, 업계나 기술을 보는 시야 등이 강제적으로 확장된다.

좁은 시야의 확장은 자신의 인생에 있어서 중요한 Value 를 찾게해주기도 한다.

당연히 비즈니스 기회가 많이 보이지 않을까?

 

그리고 가장 큰 장점으로는 이직을 목표로 하고있다면 당연히... 전직장 연봉은 자신의 몸값에 큰 지표가 된다. ^^;;

사회 생활을 해본 분들은 절감하겠지만... 시장에서 자신의 가치는 올리기가 정말 쉽지않다.

대기업에서는 평균적으로 높은 테이블의 연봉을 보장하며, 이는 나와서도 도움이 될 수 있다.

 

- 단점

주도적이 되지 못할 가능성이 크다. 이것도 사람마다 하기 나름이고 회사 나름이라 일반화시킬 수는 없다.

하지만, 우리나라 회사에서는 대게 시키는 일을 하게 된다.

 

대기업은 모든 체계에 대한 작업이 이미 이루어져있는 경우가 많다.

가령 당신이 입사해서 당신이 회사에 기여할 수 있는 훌륭한 코드를 만들 수 있다고 할 때, 이미 해당 기능은 비슷한 생각을 한 누군가에 의해 만들어져 있거나 다른 훌륭한 오픈소스가 사내에 적용되어 있을 가능성이 높다.

그렇기 때문에 이 때 당신의 능력은 회사에 '당신만의 훌륭한 것' 을 기여하기보다는 '회사의 훌륭한 시스템' 을 얼마나 잘 활용하는지로 계발되고 평가되어질 가능성이 높다.

그렇기 때문에 자신이 빛나고 싶다면 회사의 시스템에 의해 역량이 묻히지 위한 노력이 필수적이다.

 

자신이 홀로 빛나고 싶다면 대기업은 쉽지않다. 물론 홀로 빛나는 천재들도 분명 계시지만...

필자를 비롯해 대부분은 그렇지 않다. ㅜㅜ

어떻게보면 경쟁이 치열하다는 의미로 해석될 수 있겠다.

 

- 대표기업 : Samsung, LG 등

 

- 전략

당연히 실력이다. 하지만 즉시 투입되어 사용할 수 있는 업무 활용능력 보다는 잠재력 이라고 하고싶다.

대기업은 원하는 인프라를 모두 제공해줄 수 있고, 직원들을 키우는데 있어서 아낌없이 투자한다.

하지만 가장 중요한건 그 투자 이상의 회수이다. 

즉, 시니어 / 임원급이 아닌 이상에야 잠재력이 뛰어난 인재를 뽑아서 키우는 전략이 주가 되기 때문에 그 점을 중점적으로 생각해보면 된다.

 

협업도구의 까다로운 설정을 능숙하게 한다던지, 데이터베이스의 쿼리나 협업도구를 훌륭하게 쓰는 건 훌륭한 자질이고 기술이지만 대부분의 대기업이 1순위로 보는 능력은 아니다.

 

특히 주니어라면 알고리즘, 자료구조 등 CS 기본기가 가장 중점이 되며 이 들을 보는 이유는 사고력과 학습능력이다.

회사에 필요한 어떤걸 주더라도 빠르게 학습하고 응용해서 경쟁력을 갖출 수 있는 인재가 되도록 준비를 해보면 좋겠다.

 

그리고 중요한건 "쫄지말자". 대기업이어도 사람 뽑는 이유는 하나다. 인재가 필요하기 때문이다.

대기업이라고 갑이고 본인이 을 인 경우가 아니며 본인이 인재라면 "갑의 입장" 이 되어볼 수 있다.

 

추천 지식 : 개발언어 기초지식, 자료구조, 알고리즘, Soft Skills (기술 이외의 것들 - 사회성이나 말하는 방법 등이 포함된다.)

 

 

보다 주도적인 개발이 좋다면 Software 개발 중심의 회사로

 

 

앞서 언급한 대기업 중에 Software 개발이 중심이 아닌 회사들도 많이 있다.

또한, 의외로 Software 개발 중심의 비즈니스 모델을 가지거나, 비즈니스 모델과 무관하게 Software 경쟁력이 뛰어난 회사들도 있다. 

 

- 장점

개인적인 경험에서 얘기를 한다면, 모든 케이스를 일반화할 수는 없지만 소프트웨어 중심의 서비스 회사에서 배울 수 있는 경험은 개발자로서 아주 소중한 경험이 된다.

개인적으로 역량을 개발하고 토이프로젝트를 진행하는 것 이상으로 팀 단위로 구성된 팀에서 잘 갖추어진 서비스를 신규 개발 및 런칭해보고 운영해보는 경험은 개인 공부로는 쌓기 힘든... 커리어에 있어서 큰 자산이 된다.

그리고 많은 회사에서는 이런 종류의 경험들은 Senior Developer 가 됨에 따라 개인의 가치를 높이는 무기가 되기도 한다.

 

또한 소프트웨어 개발자로서 얻을 수 있는 Domain 지식이 좀 더 많을 수 있다.

이부분은 연구중심의 회사와 서비스중심의 회사가 다를 수 있는 부분이라 "Case by Case" 라고 하겠지만,

Software 개발에 있어서는 도메인적 지식의 폭이 더 넓어지고 깊어질 수 있다.

당연히 미리 만들어진 체계를 쓰는 대신 새로 자신이 공부해서 만들어내거나 신기술을 적용할 수 있다면, 그 자체로도 도메인적 지식이 습득되고 자기발전으로 이어지지않을까?

 

자신이 욕심내서 하는만큼 실력이 발전하고, 좋은 회사라면 자신의 개발 실력에 알맞는 평가까지도 받을 수 있는 좋은 선택지가 될 수 있다.

 

- 단점

업무가 많을 가능성이 정말 크다.

특히 잘 짜여진 체계가 없는 회사이거나 개발자 개인의 역량이 아주 중요한 스타트업에 경우...

개발자는 슈퍼맨이 될 가능성이 크다.

어쩌면 Software Architecture 의 중대한 부분에 대한 의사결정부터, 코드 레벨의 마이크로튜닝까지를 모두 담당하게 될 것이고, 비즈니스 환경에서 계속 바뀌는 요구사항은 수시로 코드를 뒤엎게 만들고 리팩토링까지 요구한다.

 

이 모든 경험이 개인의 발전에 있어서 큰 도움이 된다고 생각할 수도 있다. (실제로도 노력은 배신하지 않는다.)

다만, 본인이 너무 힘들다면 생각해보자.

우리는 개발하기 위해 태어난 사람이 아니라 행복하기 위해 노력하는 사람들이다.

 

- 대표기업 : Naver, Kakao 등

 

- 전략

많은 경우 개발자로서 대기업에 입사하는 것보다 쉽지않다.

네임 밸류만 보고 무시하고 대충 준비하는 사람들이 간혹 보이는데...

큰코 다칠 정도로 난이도가 있는 경우가 대부분이다.

잠재력도 물론 중요하지만 업무에 바로 투입될 수 있는 업무 활용능력이 좀 더 중요하다.

 

요구하는 인재상에 따라 다르겠지만 예시를 들자면, 주어진 문제에 대한 해결 능력에 대해 토론할 일보다 프레임워크의 특성을 설명해볼 일이 많다.

(물론 문제해결능력은 매우 중요하다.)

그렇기 때문에 자신의 실력을 증명할 수 있는 포트폴리오나 Github 같은 부분들은 큰 도움이 될 것이다.

필자처럼 블로그를 운영해보는 경험도 면접시에는 큰 도움이 된다.

 

추천 지식 : 개발언어 기초지식, 자료구조, 운영체제, 네트워크, 도메인 지식들

 

 

그 외...

 

경험해본 내용을 바탕으로 대기업과 소프트웨어기업으로 분류해서 얘기를 해보았다.

많은 질문을 받는 외국계에 대한 내용도 향후 기술할 수 있었으면 좋겠다.

필자는 현재 회사에 열심히 적응 중이고, 많은 것을 배워나가는 중이다.

향후 외국계에 대해서도 포스트를 업데이트하게되길 바란다. :)

 

 

하지만...?

 

 

"포스팅에서 기술한 기업 타입 별 장/단점이나 면접 준비 전략 모두" 일반화시킬 수 있는 지식이 아닌 필자의 개인적인 경험이라고 봐주시면 좋겠다.

빠져나갈 구멍을 만드려는게 아니라 실제로 그렇다.

회사마다 조직별로, 부서별로 특색은 정말 천차만별이며 특히 면접은 면접관의 가치관이 반영될 수밖에 없다.

 

그리고...

 

가장 중요한 건 자신이 어떤 사람이며 어떤 커리어를 추구하는 지를 생각해보는 것. 그 자체인 것 같다.

정답은 없다. 개발자로 시작했지만 다른 커리어를 밟아도 좋고, 다른 커리어를 밟다가 뒤늦게 개발을 시작해도 늦지않다.

또 어떤 회사에서 어떤 일을 하는지도 여러분의 가치를 정해주지 않는다. 가치평가는 스스로만 내릴 수 있다.

사회생활을 시작한다면 어떤 일을 할 때 좀 더 행복할 수 있는지, 자신의 인생에 있어서 더 중요한 목표가 무엇인지 고민해볼 시기이다.

 

 

마지막으로...

 

면접 탈락은 은근히 큰 내상으로 다가온다.

자존감이 낮아지는 것도 문제이지만 특히 이직이라면 면접 때마다 휴가를 써야하지 않은가...?

눈치보면서 휴가 썼는데 사소한 실수로 탈락의 고배를 마시는 건 정말 속쓰린 일이다. 

또, 만약 비전공자라면 어떻게 준비를 해야하는지부터가 막막할 수 있다.

 

절대 겁먹지말고, 탈락에 슬퍼하지 말자.

필자도 본인이 원하던 회사에 입사하기 전까지 무수한 탈락의 고비를 마셨다.

실패를 담담하게 받아들이고 보완해서 재도전하면 될 일이다.

어렵기로 유명한 "소울류 게임" 을 죽지않고 깬다면 게임을 할 이유가 없다.

 

 

개발자로 처음 발을 디딘 이들에게 위의 이야기가 도움이 되었으면 좋겠다.

 

 

SOA(Software Oriented Architecture) 란, 2000년대 초반부터 IT 업계 전반에 걸쳐 녹아든 IT System 의 패러다임이다.

 

고전의 Client-Server Architecture 에서 EJB 로 대표되는 n-Tier Model Architecture 로 진화한 웹 아키텍쳐는 2000년대 이후부터 비즈니스 요구사항에 발빠르게 대처하기 위한 구조로 Service-Oriented Architecture 라는 아키텍쳐로 진화한다.

 

SOA 란, 그전까지의 Application 의 Massive 한 기능들을 비즈니스적인 용도로 분류하고 기능 단위로 묶어서 하나의 표준 Interface 를 통해 각각을 "서비스" 로써 조합하는 Software Architecture 이다.

 

IT 업계의 요구사항이 많아지고 변화가 빨라짐에 따라 이에 대처하기 위해 나타난 Architecture 이고, 구성요소를 3가지로 분류한다.

 

1. Service Consumer - 서비스의 사용자. 사용자는 실제 클라이언트(End-User) 또는 다른 서비스가 될 수 있다.

2. Service Provider - 서비스 사용자에 요청에 맞는 서비스를 제공. Provider 는 다른 Service 의 Consumer 가 될 수도 있다.

3. Service Registry - 서비스의 정보를 저장한다. Provider 가 Registry 를 통해 Consumer 에게 서비스를 제공한다.

 

여기서 중요한건 Service Provider 와 Service Consumer 로, 이들은 실제 서비스의 유저 뿐 아니라 소프트웨어 아키텍처 내부의 "모듈" 도 대상이 될 수 있다. 

즉, SOA 는 비즈니스적으로 구분된 Service 들을 느슨하게 연결하며, 각 컴포넌트를 독립적으로 운용하여 조립이 가능하게끔 한다.

 

일반적으로 Service 를 위해 서비스 기능별로 모듈을 분리하고, 각 모듈이 다른 모듈과 상호작용할 수 있도록 만들어진 Architecture 를 SOA 라고 이해하면 된다.

 

여기까지 이해했으면 떠오르는 최근의 Architecture 모델이 있을 수 있다.

바로 Micro Service Architecture(MSA) 이다.

 

Micro Service Architecture 역시 각 서비스를 독립적으로 운용하며 서비스들을 조합하여 End User 에게 서비스를 제공하는 형태로 이루어진다. 그렇다면 이는 SOA 와 같은 아키텍처인가?

 

결론부터 말하자면, Micro Service Architecture 는 SOA 의 부분집합이라고 할 수 있다.

정확히는 SOA 는 패러다임이며, 그를 위한 Architecture 의 초안이고, Micro Service Architecture 는 SOA 의 패러다임을 따르되, 그 아키텍처를 따르지 않는다.

 

SOA 와 MSA 의 차이점

 

1. SOA 는 모듈의 의존성은 줄이되 모듈 내에서 공유할 수 있는건 최대한 공유하는 정책을 사용한다.

반면, MSA 는 가능한 공유하지 않고 모듈들이 독립적으로 운용될 수 있도록 아키텍처를 디자인 한다.

 

2. SOA 는 서비스의 Flow 를 유지하려하지만, MSA 는 Flow 의 구별을 요구한다.

가령, 서비스 내에서 결제를 하고자 할때, SOA 는 관련된 루틴을 수행하여 결제를 지원함으로써, 유저에게 제공해주는 "서비스" 를 1차 목적으로 한다.

반면, MSA 는 유저에게 관련된 루틴과 결제 루틴을 별도로 이용하게끔 한다. 즉 서비스 내의 독립이 아닌 독립된 서비스를 지향한다.

그렇다보니 SOA 아키텍처는 대게 어느정도 업격한 Protocol 과 Message 체계를 운용하게 되고, MSA 의 경우 별도의 체계가 없이 경량화된 프로토콜을 통해 운용되게 된다.

 

3. SOA 는 서비스들의 재사용에 중점을 두지만 MSA 는 서비스들의 독립을 추구한다.

이는 블로그 내 다른 포스팅에서 언급한 적 있는 Monolithic Architecture 와 유사한 부분으로, SOA 는 MSA 에 비해 보다 Monolithic Architecture 에 가깝다.

(참조 : https://jins-dev.tistory.com/entry/%EC%A0%84%ED%86%B5%EC%9D%98-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%AA%A8%EB%8D%B8-%EB%AA%A8%EB%86%80%EB%A6%AC%EC%8B%9DMonolithic-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98)

 

 

아키텍처를 디자인하는 일은 늘 어려운 일이고, 실무에서의 경험을 상당히 요하는 일이다.

아키텍처의 디자인과 이해를 위해서는 최신 트렌드 뿐 아니라 과거의 모델까지도 숙지해두는 것이 필요하겠다.

 

좀 더 자세한 설명은 다음 링크를 참조해보자.

https://dzone.com/articles/microservices-vs-soa-2

https://www.bmc.com/blogs/microservices-vs-soa-whats-difference/

 



메시지 지향 미들웨어(Message Oriented Middleware)란 독립될 수 있는 서비스간에 데이터를 주고받을 수 있는 형태의 미들웨어를 말한다.


구성요소간 통신을 하는 방법에 있어서 네트워크(Network) 를 이용하거나 Process 간 통신 등의 중계를 해주는 미들웨어의 경우에도 Message Oriented Middleware 라고 부를 수 있지만, 일반적으로 Server to Server 로 전달되는 Message 서비스를 말한다.


메시지 지향 미들웨어의 사용은 통신을 통해 Service 들 간의 분리와 독립, 그럼에도 불구하고 안정적인 기능을 제공해줌으로써 Software Architecture 를 설계하는 데 큰 유연성을 제공해준다.


메시지 지향 미들웨어는 대부분 메시지큐(Message Queue) 의 형태로 구현되어 있으며 줄여서 MQ라고 부른다.


MQ의 종류에는 흔히 알려진 Rabbit MQ 가 있으며 이런 종류의 메시지큐들은 AMQP(Advanced Message Queueing Protocol) 를 이용하여 구현되어 있다.


MQ 시스템을 인프라에 구축했을 때에 아키텍처상으로 이를 브로커(Broker)라고 부르기도 한다.


Message Queue 를 이용할 때 얻을 수 있는 장점으로는 다음과 같은 것들이 있다.


(1) Redundancy : 메시지의 실패는 보관 및 관리가 되며 메시지가 실패한다고 하더라도 시스템에 영향을 주지 않는다.


(2) Traffic : 메시지 큐에 메시지를 쌓아두면 무분별하게 증가할 수 있는 트래픽에 대해 느리더라도 안정된 처리가 가능하다.


(3) Batch : 메시지 큐는 메시지에 대한 일괄처리를 수행하며 특히 부하가 심한 Database 에 대한 처리에 대한 효율성을 기대할 수 있다..


(4) Decouple : 소프트웨어와 MQ는 분리되며, 이는 소프트웨어 설계에 큰 이점을 준다.


(5) Asynchronous : 큐에 넣기 때문에 영향을 받지않고 비즈니스 로직을 처리할 수 있다.


(6) Ordering : 트랜잭션의 순서가 보장되며 동시성 문제에서도 안전하다.


(7) Scalable : 다수의 서비스들이 접근하여 원하는 서비스를 이용하는데 용이하다. 이는 다수의 서비스를 분리하는 데에도 큰 도움을 준다.


(8) Resiliency : 연결된 서비스에 이상이 생기더라도 다른 서비스에 영향을 미치지 않는다.


(9) Guarantee : MQ 는 트랜잭션방식으로 설계되어 있으며 메시지 처리의 트랜잭션을 보장한다.


이러한 장점들을 갖고 있기 때문에 주로 API 서버를 구축할 때 중요하거나 그 자체로 무거울 수 있는 기능들을 별도의 서버로 분리하고, MQ 를 통해 통신하는 방식이 선호된다.


이는 프로젝트의 규모가 커짐에도 유연성을 확보할 수 있는 좋은 수단이며 서비스 자체에서 처리하기 부담스러운 요구사항들을 만족시킬 수 있는 옵션을 제공한다.



다음 링크들을 참조했습니다.


https://docs.oracle.com/cd/E19435-01/819-0069/intro.html

https://stackify.com/message-queues-12-reasons/

https://heowc.tistory.com/35





CQRS(Command and Query Responsibility Segregation) 란 .Net 기반으로 발전되고 있는 설계 방법론으로 명령과 쿼리의 역할을 구분하는 것이다. 


이는 데이터에 대한 조작 Create, Insert, Delete 와 데이터에 대한 조회 Select 를 구분하는 것에서 출발한다.


어플리케이션을 개발할 때, 컨텐츠를 위한 데이터 모델은 계속해서 복잡도가 올라가게 된다.




특히 주로 사용되는 위의 모델처럼 데이터 변경과 조회는 보통 하나의 데이터모델을 사용하게 되는데, 어플리케이션의 복잡도가 증가할 수록 각 API 기능의 책임이 어떤 데이터 모델에 있는지는 불분명해진다.


이는 설계에 있어서 초기 의도를 지워버리는 역할을 하며 많은 경우의 레거시 코드들이 이런 기반으로 생겨나게 된다.


CQRS 는 이러한 고민에서 출발하며, 데이터에 대한 조회(Query) 와 데이터에 대한 조작(Command) 을 분리함으로써 이 문제를 해결하고자 한다.


기본적으로 CQRS 를 적용하기 위해서는 Command 을 위한 도메인 모델과 Query 를 위한 도메인 모델을 분리한다.



분리된 각각의 도메인 모델을 DB에 적용하는 방안으로는 몇가지가 있다.


(1) Simple


 : 같은 Scheme 을 가진 DB를 사용하며, Command / Query 시에 데이터에 대한 Converting 을 거친 후 DB에 CRUD 에 대한 작업을 수행한다.

 이 경우에는 일반 어플리케이션과 같으며 도메인 모델만 분리한 상태로 개발이 쉽고 적용이 간단하다.



(2) Premium


 : Command 를 위한 DB와 Query 를 위한 DB를 분리하는 형식으로, 데이터의 정합성을 위한 RDB를 Command 용 DB로 분리하고 Query 가 간편한 NoSQL 을 Query 용 DB로 주로 사용한다.

 이렇게 동일한 데이터에 대해 다수의 저장소를 운용하는 방식을 Polyglot Storage 라 하며 이 경우 용도에 맞는 저장소를 골라서 좀 더 알맞게 사용이 가능하다.

 하지만 분리된 저장소 각각에 대한 데이터 동기화 이슈를 Broker 등을 이용해 처리해주어야 하는 점은 이슈가 되며 책임의 소재나 로깅 등에 있어 신뢰도 확보를 위한 작업이 필요하다.



(3) Event Sourcing


 : Event Sourcing 이란 Application 내의 설계를 컨텐츠 기반이 아닌 기능 기반으로 하면서 이러한 "이벤트(Event)" 자체를 DB에 저장하는 방식을 말한다.

 이렇게 함으로써 이벤트에서 사용하는 도메인 모델은 컨텐츠를 위한 DB에 Write 되고 Query 시에는 이벤트를 저장한 DB로부터 해당 컨텐츠를 바탕으로 데이터를 만들어서 가져온다.

 도메인 모델에 대한 Command 가 따로 저장되고, Query 를 위한 도메인 모델은 Event DB로부터 불러오는 방식 때문에

 Event Sourcing 의 Architecture를 적용함에 있어서 CQRS 는 필수적인 설계 방식이 된다.

 CQRS 를 적용하는 데 있어서도 가장 큰 시너지를 낼 수 있는 Architecture 의 하나이다.


<향후 Event Sourcing Architecture에 대해서는 추가로 정리한다.>


CQRS 가 실무에 적용되는 데 있어 아직은 국내외적으로 불확실성이 있는 듯 하지만, 주목해볼만한 패턴인 것은 틀림없다.


(참고자료 : https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn568103(v=pandp.10))


 마틴 파울러가 재창한 두개의 개념으로, 비즈니스 로직을 처리하는 두가지 패턴을 말한다.

이는 책임지는 쪽이 Domain Level이냐 Script Level이냐에 따라 구분된다. 가령 다음과 같은 예시가 있다고 가정해보자.


먼저 트랜잭션 스크립트란, 은행의 계좌 이체 서비스처럼 하나의 트랜잭션 안에서 필요한 모든 로직을 수행하는 패턴이다. 

그래서 이름도 Transaction Script 로 부른다. 구현이 매우 쉽고 단순하지만 구조가 복잡해질 수록 모듈화의 복잡도 역시 높아진다.

다만, 하나의 강건한 로직이 해당 모듈에서만 구현되어야할 경우 side effect 를 예방할 수 있고, 좀 더 효율적인 코드를 작성할 수 있다.


도메인 모델은 우리가 흔히 객체 지향의 예제로 많이 배우는 형태의 모델로, 각 객체에 객체가 수행해야 하는 업무를 분담시키는 것이다. 

주요 특징은 데이터와 프로세스가 같이 존재한다는 점이며, 객체간 관계를 맺을 수 있어, 제약하거나 로직의 단순화에 도움이 된다.


또한 주로 같이 사용되는 상속이나 다양한 패턴들이 객체를 중심으로 얽히면서 개발자가 선택할 수 있는 유연성이 높아지고 모듈화가 간단해진다. 유지보수가 편리하며 재사용성이 뛰어나다.


 하지만 객체들간의 설계가 수반되어야하기 때문에 모델 구축이 쉽지 않고, 객체간의 관계를 잘 풀어나가야 한다. 특히 객체간의 dependency 는 반대로 말하자면 제약사항이기 때문에 설계가 매우 중요하다.


도메인 모델과 트랜잭션 스크립트 패턴의 차이를 보기 위해 다음 예제를 확인해보자.



    Emp emp = loadEmp("나직원");
    Dept dept = loadDept("A부서");



이 때 이를 비교 하기 위한 Domain Model 방식과 Transaction Script 방식의 차이는 다음과 같다.



    //Transaction Script 방식
    if(emp.getDeptName() == dept.getDeptName())
        return true;

    //Domain Model 방식
    if(emp.isBelongTo(dept))
        return true;



즉, Function의 책임 소재를 어디에 두는가, 사용자가 작성하는 코드 로직에 두는가(Transaction Script 방식) 아니면 객체 자체의 모델링 자체에 두는가(Domain Model 방식)의 차이이다.


참조

http://javacan.tistory.com/entry/94

http://lorenzo-dee.blogspot.com/2014/06/quantifying-domain-model-vs-transaction-script.html





회사마다 사용하는 테스팅의 종류가 다를 수는 있으나, 여지껏 다니던 회사들에서 주로 사용하던 테스팅 방법 들을 정리해보았다.


회사마다 QA 팀이 갖추어져 있는 경우도 있고 스타트업의 경우 별도의 QA팀 없이 개발팀에 해당 책무가 주어지는 경우가 있다.


다음은 몇가지 테스팅의 종류들이다.


(1)   Sanity Testing : 새로운 버전이 주요 테스트를 수행하기 적합한지를 판단하기 위한 테스팅. 만약 사용 초기의 Crash 등으로 프로그램이 사용불가능하다면 시스템은 테스팅이 불가능하다. , QA 위한 테스트라 있다.


(2)    BlackBox Testing : 내부설계 구현은 고려하지 않고 요구사항과 기능성에만 기반한 테스팅을 수행한다.


(3)    WhiteBox Testing : 글래스박스 테스팅이라고도 하며, 내부로직에 대한 지식을 기반으로 코드구문, 분기, 조건 등에 대한 커버리지를 포함한다.


(4)    Unit Testing : 개발자에 의해 수행되며 소프트웨어 모듈 각각에 대한 세부적 테스팅


(5)    End to End Testing : 주로 네트워크, DB 연동, 시스템간 커넥션에 대한 테스팅이다.


(6)    Load Testing : 부하가 걸린 상황에서 시스템의 동작을 점검한다.


(7)    Stress Testing : 허용 범위를 초과하여 한계치를 측정하는 테스팅이다.


(8)    Alpha Testing : 개발 부서 내에서 만든 가상환경에서의 테스팅


(9)    Beta Testing : 상용화 릴리즈 이전 end user 의해 완료되는 테스팅


(10)  Recovery Testing : 시스템의 회복을 테스팅한다.


(11) Smoke Testing : Sanity Test와 유사하게 테스트가 가능한지 여부를 판단한다. (연기가 나는지를 판단하는 작업) 여러 개의 스모크 테스트를 스위트라고 한다.


 * Sanity Test와 Smoke Test 의 차이는 기능 검사에 있다. Sanity Test가 새로 테스트 항목에 추가된 추가된 기능 / 수정된 버그 에 초점을 둔다면 Smoke Test 는 프로그램의 핵심적인 부분을 같이 검사한다. (즉, 새로운 기능추가가 기존의 핵심 기능에 영향을 안미치지는지 테스트 한다.)

또한, 같은 QA 이전의 테스팅이지만 주로 Smoke Test 는 개발팀 내에서 직접, Sanity Test 는 개발팀에서 기능 검사만 하고, QA팀으로 넘기는 경우가 많다.

(보다 자세한 비교는 다음 링크가 잘 설명해주고 있다.)

 - 참조 : https://www.guru99.com/smoke-sanity-testing.html





TCP/IP 전송제어 프로토콜은 신뢰성에 초점을 맞추고 데이터가 신뢰성있게 올바른 순서로 유실되지 않게 빨리 전송할 수 있도록 설계된 프로토콜이다.


TCP 의 기본적인 작동 원리는 다음과 같다.

<TCP hand shake >

<출처 : https://wiki.mikrotik.com/wiki/Manual:Connection_oriented_communication_(TCP/IP)>


TCP 는 통신을 위해 3 way handshake 란 방식을 이용한다. 먼저 통신이 가능한지 SYN 을 보내고, 통신이 가능하다는 SYN-ACK을 받는다. 이 과정에서 문제가 없으면 이제 통신하고자 하는 메시지를 보내고, 양방향 통신이 된다. 이 과정을 3-WAY Handshake 라 하며, 이 과정은 TCP 매 통신 시마다 적용이 되며, 통신이 끝날때에는 SYN 대신 FIN 을 보내서 서로 확인을 받는다. 이러한 과정을 통해 TCP 는 무조건 메시지가 잘 전달되었으며 잘 도착했다는 확인을 받게 되고, 신뢰성을 보장받게 된다. 이러한 특성으로 TCP는 주로 전화에 비견된다. 이러한 TCP 통신을 위한 헤더는 다음과 같다.



위의 헤더 정보에는 보내는 메시지의 목적지 주소와, 순서 보장을 위한 일련 번호(Sequence Number), 에러 체크를 위한 Checksum 정보 등이 포함된다.

그 외에 사용하는 TCP 알고리즘 별로 슬라이딩 윈도우 정보 등 다양한 정보가 포함될 수 있다.


 슬라이딩 윈도우는 패킷의 흐름을 제어하기 위한 메모리 버퍼(Window) 로, 다양한 경우에 대비해서 사용된다. 가령 3WAY Handshaking 중 네트워크 장애 발생이나, 받은 메시지가 중간에 누락이 된 경우 해당 패킷의 재전송이 필요하다. 다음 패킷의 전송을 멈추고 이전 패킷의 전송을 기다리는 Stop And Wait 방식 보다, 훨씬 효율적일 수 있다.


<슬라이딩 윈도우 송신 측>


 위의 그림 처럼 슬라이딩 윈도우는 이미 전송하고 확인이 완성된 부분, 전송했지만 확인되지 않은 부분, 보내지 않았지만 수신자가 수신 준비 된부분, 수신준비 되지 않았으며 송신하지 않은 부분으로 영역을 나누어 네트워크 상황에 따라 포인터를 관리한다.


<슬라이딩 윈도우 수신 측>


 위의 그림은 수신측이다. 받았고 승인한 버퍼를 갖고 있으며 ( 이 버퍼는 나중에 다른 패킷으로 덮어써진다.) 수신자가 아직 받지 못한 부분, 수신준비되지 않았으며 수신하지 않은 부분으로 나뉘며 전송 상태에 따라 포인터를 조정한다.

(관련 내용의 예시를 잘 정리한 블로그는 다음을 참조하기 바란다. http://blog.naver.com/donjobani/30110435544)



 위의 내용들을 바탕으로 한 TCP 의 특징을 정리하자면 다음과 같다.


(1) Connection Oriented : 2개의 endpoint간 연결을 먼저 맺고 데이터 통신을 한다.


(2) Bidirectional byte stream : 양방향 Data stream(Byte stream)


(3) In-order delivery : 송신자가 보낸 순서대로 수신하며 Segment의 데이터 순서 표시를 위한 32비트의 정수를 사용한다.


(4) Reliability through ACK : 송신자는 수신자가 ACK 보내는 것을 체크하고 보내지 않은 데이터를 보관한다.


(5) Flow control : 송신측과 수신측의 처리 속도 차이를 해결하기 위해(특히 수신측이 느린 경우) 지원하는 제어 방식으로 흐름 제어라 불린다. 속도 차이로 전송 누수가 생기는 것을 방지하기 위해서 수신자는 받을 수 있는 버퍼의 크기(Receive window)를 송신자에 전달하여 송신자가 보내는 양을 제어하게 한다.


(6) Congestion control : 통신시 한 Router에 데이터가 몰려 혼잡할 경우, 호스트 들이 데이터 유실 때문에 재전송을 하게 되고 결국 혼잡이 더 늘어나는 악순환이 생긴다. 이를 막기 위해 송신측에서는 Congestion window가 존재하여 허용하는 만큼만 전송하게 하여 혼잡을 제어한다. TCP Vegas, BIC, CUBIC 등의 알고리즘을 사용한다.



 반면 UDP는 이러한 Hand shaking 과정이 없다. UDP 의 통신과정은 단순하다. 단순히 명시받은 IP 주소와 포트로 메시지를 UDP 헤더를 담은 메시지를 보낸다. UDP 헤더가 담긴 메시지를 받으면, 어플리케이션은 해당 메시지를 해독해서 로직을 처리한다. UDP 헤더는 다음과 같다. TCP 헤더보다 많이 단순한 모양이다.


 하지만 TCP 와 다르게 UDP 는 응답값을 기대하면 안된다. 신뢰성이 보장되지 않기 때문에, 중간에 네트워크 이슈로 인해 데이터가 손실되더라도 이를 알아차릴 방법이 없다. 그렇게 때문에 UDP 는 편지에 비유되며, 상대방 IP 주소의 포트에 메시지를 놓고온다고 이해하면 쉽다.


 정리하자면, TCP의 경우 좀 더 시간이 오래 걸리는 무거운 프로토콜이지만 신뢰성이 보장되며 UDP 의 경우 가볍지만 신뢰성을 보장할 수는 없다. 이런 특징 때문에 신뢰성이 중요한 메시지 들은 TCP로, 몇몇 패킷이 누락되어도 상관없는, 가령 이미지나 실시간 스트리밍 들은 UDP 로 구현이 된다.

 물론 몇몇 서비스의 경우에는 비즈니스 로직에 따라 UDP 로 구현하되, 헤더를 커스터마이즈하여 특정 조건에서 신뢰성을 갖으면서 가볍고 빠른 자체 프로토콜을 만들어 서비스 품질을 높이는 경우가 많다.







사실 MySQL은 이미 프로그래밍을 모르는 사람들도 들어봤을 정도로 너무 유명한 Database라 MySQL이 무엇인가를 얘기하는 건 진부한 주제일 지 모른다.


그렇듯 MySQL은 우리가 흔히 "데이터베이스" 라고 하는 시스템의 표준과 같은 소프트웨어이며 그만큼 RDBMS 중 세계적으로 가장 널리 사용되고 있는 소프트웨어이다.


RDBMS에 대해서 간단히 언급을 하자면, 저장한 데이터들 간의 관계를 명시하는 "관계형 데이터 모델링" 을 지원하는 DataBase Management System 이다. 데이터는 테이블에 명시된 여러 Column 값들을 포함하는 Tuple 또는 Record 로 구성되어 Row를 이룬다. 말이 좀 복잡하지만, 간단히 얘기하면 정의된 포맷대로 나열된 데이터의 목록이라고 생각하면 쉽다. 가령 Id 와 Password 를 저장하는 유저 정보의 데이터는 다음과 같이 나타내어질 수 있다.



위의 그림에서 테이블이 나타내는건 유저의 ID와 Password 이다. 이것이 일반적인 DBMS, 즉 데이터베이스 관리 시스템의 모습이다. ID 와 PASSWORD는 테이블을 구성하는 데이터를 묘사(Description)하는 데 기준이 되는 Column 이고, 그 아래의 Record 들은 실제 데이터가 담긴다. 적층된 데이터의 행들이 ROW 이다.


그렇다면 여기에 "관계" 를 추가해보자.




왼쪽 그림과 같은 기존의 유저 ID 관리 테이블에 이번엔 오른쪽의 유저 정보 테이블을 추가했다. 

그런데 자세히 보면, 같은 Column 에 같은 데이터를 갖는 Row를 확인 할 수 있다. 가령 "a123de" 라는 유저와 "jinsp" 라는 유저는 양쪽 테이블 모두에 있으며, 왼쪽 테이블에서는 해당 ID의 비밀번호를, 오른쪽 테이블에서는 해당 ID의 이름과 나이 정보를 알 수 있다.

이렇게 두 테이블은 ID라는 데이터를 기준으로 하는 "관계(Relation)" 를 갖고 있다고 할 수 있으며 RDBMS 는 이런 기초적인 관계에 대한 고민에서 출발한다.


MySQL은 이러한 RDBMS 중에서도 가장 널리 쓰이고 잘 알려져있으며 심지어 무료로 제공되는 훌륭한 소프트웨어라고 할 수 있다. (그 외에도 장점은 많지만, 차차 기술하도록 한다.)


실제로 실무에서도 대부분 MySQL 을 사용하며, 같은 갈래에서 나온 MariaDB나, PostgreSQL 과 같은 DBMS들도 MySQL 에서 상당 부분 아이디어를 공유한다. (물론 각 DB들에 따라 특색은 있다.)



MySQL 을 설치하게 되면 패키지 형태의 프로그램이 깔리게 되며, 이는 여러 가지 단위 모듈들로 구성되어 있다.


<그림 - 출처 MySQL 홈페이지>


엔진을 담당하는 MySQL 은 DB 자체이며, DB를 접근하기 위한 서버와 클라이언트의 형태를 제공한다. 기본적으로 DB에 접근하기 위한 인터페이스를 서버로 두고 있다. 이 서버는 기본 설정에 따르면 localhost IP 에 3306 포트로 열려있으며 이를 통해 MySQL 클라이언트가 이에 접근(Connect) 하여 질의(Query) 하는 구조로 되어 있다. 이때 질의하는 언어는 SQL이다.


즉, 외부에서 드라이버 또는 통신으로 접근 시에도 마찬가지로 TCP/IP 를 통해 MySQL 패키지의 클라이언트 또는 서버로 접근하여 MySQL DB 자체에 접근하게 되는 것이다. (당연해보이는 개념이지만, 개발 시에 SDK 형태로 제공되는 Driver 의 동작원리를 이해하는 것은 중요하다.)


MySQL Server 를 설치하게 되면, MySQL 서버 프로그램으로 MySQL 서비스와 mysqld 프로그램, MySQL 클라이언트 프로그램으로 mysql.exe(CLI 인터페이스), Workbench, slap, mysqladmin 등이 같이 설치된다.






 모놀리식 아키텍처란, 마이크로서비스의 각광에 따라 마이크로서비스가 아닌 전통의 아키텍처를 지칭하는 의미로 생겨난 단어이다. 위의 그림에서 처럼 모든 모듈은 서비스 내부의 Product 형태로 종속되어 있으며, 서비스에만 집중할 수 있는 구조로 되어 있다.


이는 Monolithic 이라는 단어의 뜻 그대로 하나의 Massive 한 Context 형태의 아키텍처를 의미하며 

하나의 서비스 또는 어플리케이션이 하나의 거대한 아키텍처를 가질 때, Monolithic 하다고 한다. 


 모놀리식 아키텍처를 갖는 Software의 특징은 그 자체로 강건하며 내부 요소간의 Dependency 를 크게 가질 수 있다는 점이다. 그리고 이는 필연적으로 구조적인 Coupling 이 강력하게 유지되는 결과를 초래한다. 


 또한 각 비즈니스 컴포넌트들이 하나의 강한 결합 구조를 지니며 통일성이 있다.

이는 비즈니스 로직이 서비스에 최적화된 코드를 만들어내는데 좀 더 집중할 수 있는 반면 복합적인 예외를 만들 수 있는 위험성을 내포하게 된다.



장점과 단점


 개발 초기에는 단순한 아키텍처 구조와 개발의 용이함이 큰 장점이지만 규모가 커짐에 따라 복잡도가 심각하게 증가한다.


 가령 토이 프로젝트를 한다고 생각해보자. 생각이 흐르는대로 Prototyping 을 할 때는 구조가 복잡하고 Dependency 가 크더라도 손쉽게 만들 수 있으며 오히려 모듈별로 분리하고 나누는 것은 코드의 최적화 및 구현에 방해가 되는 경우가 많다.


 그러나 프로토 타이핑 이후에 새로운 컨텐츠를 추가하거나 새로운 팀원이 생겼을 때 문제는 시작된다. 이 거대한 구조가 본인에게는 간단하고 쉬울 수 있으나 새로운 팀원에게는 가혹하게 느껴질 것이다.


 또한 최근 클라우드 환경이 각광받으면서 두드러지게된 단점으로 하나의 모듈을 수정하기 위해서는 전체 어플리케이션의 배포가 수반되며 서버 기동, 빌드 및 배포 시간이 오래걸린다는 점이 있다.


마이크로 서비스 아키텍처 관련 포스팅에서 한번 더 언급을 하겠으나, 일반적으로 마이크로서비스 아키텍처의 Scalability 복잡도가 N+M 이라면 모놀리식 아키텍처의 복잡도는 N*M 형태로 증가하기 때문에 컨테이너의 과부하와 배포 및 스케일링의 어려움을 겪게 된다.


또한 기술 스택의 선택폭이 좁아지며 많은 문제를 해결하기 위해 보다 강력하고 탄탄한 기술력이 요구된다. 이는 내부 구성요소들 간의 강력한 Dependency 문제 때문이다. 한 모듈의 선택은 다른 외부 모듈에서 버그를 초래할 수 있다. 따라서 사용하고자 하는 프로젝트의 큰 그림이 아키텍처 구성 단계에서 그려져 있어야 문제를 최소화할 수 있다.


 최근에는 많은 서비스들이 초창기에 모놀리식으로 개발되고 향후 마이크로서비스 아키텍처로 구조를 변경한다. 

혹은 인프라가 잘 갖추어진 회사에서라면 여러분을 도와줄 많은 플랫폼들이 이미 마이크로 서비스 아키텍처로 존재할 것이다.






IP 주소를 이해하는데 있어 중요한 개념이 서브넷 마스크의 개념이다.



많이 알려져있듯이 서브넷 마스크는 커다란 네트워크를 다루기 위해 서브넷으로 네트워크들을 분리하고 나누어진 네트워크를 지정(designated) 하는 데 사용된다. 


서브넷 마스크는 서브넷을 분리하기위한 기준으로 클래스별로 어디까지가 네트워크 부분이고 어디까지가 호스트부분인가를 나타낸다. 


서브넷 마스크는 다음과 같은 형태를 띈다.


Class A: 255.0.0.0


Class B: 255.255.0.0


Class C: 255.255.255.0


클래스 A가 명시하는 바는, IP주소의 첫 8개 비트가 네트워크 영역을, 나머지 24 비트가 호스트 영역을 나타낸 다는 것을 의미한다. 


여기서 기존의 IP 주소와 자신이 갖고 있는 서브넷 마스크를 AND 연산 하면 네트워크 주소를 얻을 수 있다.


AND 연산이 갖는 의미는 자신이 가진 IP 주소의 1만큼의 부분, 즉 AND 연산으로 살아남는 부분들은 네트워크의 주소라는 일종의 Masking 이 된다. 


그리고 나머지 네트워크 주소가 아닌 호스트의 주소가 의미하는 바는 해당 서브넷에서 호스트로 할당할 수 있는 IP 주소의 범위가 된다. 


이 때 얻어진 네트워크 주소에서 Subnet Mask 의 0으로 된 비트를 모두 1로 바꾸어주면, 즉 마지막 주소가 브로드캐스트 주소가 된다.




가령 위와 같은 IP 주소가 Class C 의 서브넷 마스크를 가질 때, 위의 AND 연산의 결과로 다음과 같은 주소를 알아낼 수 있다.

(AND 연산은 연산하고자 하는 두 비트가 모두 1이면 1을, 그렇지 않으면 0을 반환한다.)


- 네트워크 주소 : 10.9.32.0

- 브로드캐스팅 주소 : 10.9.32.255

- Gateway 주소 : 10.9.32.1 (끝자리만 다른형태로 일반적으로 1)


이렇게 서브넷 마스크를 이용해서 서브넷을 나누는 것을 서브넷팅 이라 한다.

위의 변환은 일반적인 Class 의 서브넷팅을 설명한 내용이고 서브넷 마스크의 기본적인 동작을 나타낸다. 위와 같이 기본 Class C 로 나눈 경우에 해당 서브넷에 할당된 주소는 256개가 된다. 만약 나누어야하는 지역의 IP 가 이보다 훨씬 수가 적다면 이렇게 나누는 것은 낭비가 될 수 있으므로, 그럴 때는 서브넷 마스크를 변경하여 호스트 수를 조절하는 방법도 가능하다. 그리고 이를 bit를 빌려온다(borrowed)라고 표현한다. (ex) 255.255.255.128


(*서브넷팅에 대한 좀더 발전된 사례는 클라우드 쪽 포스팅에서 다룰 수 있을 듯 하다.)



위와 같은 원리로 분리된 서브넷 들을 연결한다면 다양한 네트워크를 연결하여 거대한 네트워크를 구성하기에 효율적이며 관리적 측면에서도 용이하다.


이렇게 분리된 네트워크가 갖는 장점은 보다 기능적인 브로드 캐스팅 도메인을 구성할 수 있고, 


좀 더 큰 의미에서 보자면 IP 주소의 절약 및 효율적인 사용에도 도움이 되기 때문이다. 


라우터를 중간에 둔 상태에서 여러 개의 간섭받지 않는 서브넷을 둔다면 큰 네트워크는 단지 대표 주소 들에 대한 서브넷을 관리하여 계층적인 구조를 이룰 수 있기 때문이다. (자세한 내용은 라우터를 다루면서 한번 더 정리한다.)



일반적인 서브넷은 IP 네트워크 주소를 지역적으로 나누기 때문에, IP가 바뀌더라도 LAN은 동일한 서브넷으로 묶인다. 





+ Recent posts