Psychological Safety – 1편 Unsafety Signals

마틴 파울러의 한 마디 : 심리적 안전감이 없는 팀은 성과를 내지못한다.

그리고 위험 신호와 개선 방법에 대한 글을 하나 소개해주네요.
Psychological-Safety

이 글은 위의 링크를 나름대로 정리한 글입니다.

성공적인 팀의 가장 중요한 지표는 Psychological Safety!

당신의 팀은 아래의 다섯 질문에 얼마나 동의하시나요?
많이 동의할수록 Unsafety한 것입니다.
1. 만약 기회를 얻은 사람이 실패했다면 비난 받을 것이다.
2. 우리팀은 새로운 멤버가 합류하기 어려운 강한 문화를 가지고 있다.
3. 우리팀은 곤란을 겪고 있는 사람을 돕는데 소극적이다.
4. 나의 기술과 재능을 활용하는 것은 팀의 목표보다 후순위이다.
5. 팀의 민감한 이슈에 대한 공개된 솔직한 대화(open honest conversations)는 불편하다.

Unsafe team의 영향력을 가상의 신입사원 카렌의 사례로 설명해줍니다.
카렌은 분산 데이터베이스의 low-level locking 줄여줄 방법을 찾았고 테스트 결과 15%의 CPU를 절감할 수 있었기에, 제품(production) 환경에도 적용하기로 마음 먹었스비다. 데이터베이스 설정 파일을 수정하면 끝이었기 때문에 코드리뷰를 받을 필요도 없었습니다. 하지만 안타깝게도 서비스 전면 장애가 짧게나마 발생했고, 동료가 문제를 발견해서 10분 이내로 원복할 수 있었습니다.

안전하지 못한 팀은 변화를 받아들이지 못하기에 망하거나 급격히 저성과를 낼것이다.

“If I take a chance, and screw up, it’ll be held against me”
주간 포스트 모텀에서 이 사고는 다루어지게 되고 엔지니어링 디렉터는 약간의 성능을 향상시키기 위해 전면 장애를 내는 것은 받아들일 수 있는 일이 아니며 그녀를 무책임한 사람으로 몰아갔습니다. 그리고 그녀는 절대로 이 일을 잊지못할 것이고 다시는 시스템을 개선하기 위해 노력하지 않을 것입니다.

“Our team has a strong sense of culture, and it’s hard for new people to join”
카렌이 받은 충격은 아무도 그녀를 옹호해주지 않았기 때문에 더욱 커졌습니다. 아무도 데이터베이스 설정 파일의 변경에 대한 코드리뷰가 없는 것을 언급하지 않았으며, 무책임한 행동과 무책임한 사람의 차이에 대해서 언급하지 않았습니다. 그 팀은 그들이 만든 시스템의 신뢰성(reliability)에 대한 자부심을 느끼고 있었으며, 그들의 명성을 지키는 것이 신입 사원보다 훨씬 더 중요했습니다.
카렌이 배운 것은 그녀의 팀과 매니저가 그녀를 돌봐주지 않는다는 것이었습니다.

“My team is slow to offer help to people who are struggling”
카렌은 제품(production)에는 처음이기에, 사고 대응이나 제품 안정성(production hygiene)에 대한 공식적인 교육을 받지 않았으습니다. 분산 시스템에 대한 문제해결은 고사하고. 그녀의 팀은 경력자로 구성되었기 때문에 트레이닝이나 신규입사자 문서가 필요가 없었습니다. 또한 카렌과 같은 신규입사자가 이러한 기술을 배우는데 시간을 보내도 된다는 지침(signal)도 없었습니다.

카렌은 가면 증후군(Imposter Syndrome: 자신의 성공이 노력이 아니라 순전히 운으로 얻어졌다 생각하고 지금껏 주변 사람들을 속여 왔다고 생각하면서 불안해하는 심리이다.)에 시달렸고, 어떻게 그녀가 입사할 수 있었는지 이해할 수 없었으며 왜 자신이 아직까지 해고되지 않는지 종종 생각하게 되었습니다.

“Using my unique skills and talents come second to the goals of the team”
카렌은 알고리즘, 자료구조, 그리고 분산 컴퓨팅을 전공했으며, 시스템이 전체적인 관점에서는 최적화되지 않았으며 부하 급증을 결코 처리하지 못할 것이라는 것을 깨달았습니다. 팀은 항상 고객이 계약보다 더 많이 사용한다며 비난했으며 이것은 마치 바베큐를 할려는데 왜 비가 오냐고 기상예보를 비난 하는 것과 같습니다.

카렌은 새로운 설계를 제안했지만, 동료들은 친숙하지 않는 새 기술을 리스크로 간주했으며 토론도 해보지 못하고 포기해야 했습니다. 카렌은 코드를 작성하고 시스템을 만들기를 원했지만 무의미한 논쟁은 원치 않았습니다.

“It’s uncomfortable to have open, honest conversations about our team’s sensitive issues”
대규모 고객 트레픽 증가가 몇 시간의 장애를 일으켰을 때, 카렌은 지금의 설계로는 절대로 문제를 해결할 수 없고 자신의 설계에 대해 말했지만 엔지니어링 디렉터는 카렌의 설계는 이미 엔지니어링 리뷰에서 기각되었다고 말했습니다.
이후에 카렌은 팀 동료에게 그 디렉터의 설계가 문제라는 것을 이해하지 못한다고 말했지만, 그 팀동료는 어깨를 으쓱이며 지금까지 잘해왔다고만 말할 뿐이었습니다.

카렌은 칼퇴하고 새로운 일자리를 찾아 떠났고, 회사는 그녀를 그리워하지 않았고 그녀를 무모하고, 불평하고 그리고 권위에 대항하는 사람으로 남게 되었습니다. 그리고 그들은 그녀의 설계가 반복되는 장애에 의한 고객 탈출을 막을 수 있는 설계라는 것을 결코 깨닫지 못했습니다.

어떻게 Psychological Safety를 만들 것인가는 2부에서 계속됩니다.
How to build psychological safety into your own team
We need to balance respect for our culture, with an openness to change it as needed.

Javascript : Prototype 체인 개념의 이해

자신이 익혔다고 알고있는 지식이 제대로 알고 있는지 확인 하고 싶다면 퀴즈를 통하면 간단히 알 수 있다.
Javascript에서 사용되는 prototype 체인 방식을 이해하기 위해 다음에 준비한 간단한 퀴즈를 풀어보자.

  • 모든 문항들은 앞의 질문과 답 이후로 이어진다.
  • 돈(money)과 통장(tongjang)의 프로퍼티를 부여한 지갑jigap 인스턴스를 만들고
  • 제인과 타잔이라는 인스턴스를 prototype으로 지갑(jigap)을 선택해 생성 하였다.

* 제인과 타잔이 프로토타입 체인을 통해 각각의 prototype인 지갑의 프로퍼티인 money와 tongjang을 상속 받게 되었다.

질문 1 : 타잔이 지갑에서 상속받은 프로퍼티 money에서 10을 사용 했다면?

질문2 : 지갑의 프로퍼티 monoey에 10을 더한다면?

질문3 : 제인이 자신의 프로퍼티 tongjang에 10을 꺼내쓴다면?

질문3 : 지갑에 뒤늦게 신규 프로퍼티 card가 추가된다면?

질문4 : 타잔이 신규 프로퍼티 rushcash를 긁어 버렸다면?

질문5 :

레저큐 인턴기 7편 – 여자 개발자, 남자 디자이너를 위하여

Culture does not make people. People make culture. If it is true that the full humanity of women is not our culture, then we can and must make it our culture.
문화가 사람을 만드는 것이 아닙니다. 사람이 문화를 만듭니다.

Chimamanda Ngozi Adichie, We Should All Be Feminists

내가 다닌 학부는 남학생 140명, 여학생이 10명의 무려 14:1라는 경이로운 성비를 자랑한다. 머릿 속에 작은 방을 그려놓고 거기에 사람 15명을 들여 보낸다면 그 중 단 한명이 여자라는 것이다.

설상가상으로 우리 학부가 위치했던 신공학관은 학교 전체에서 제일 남녀 성비가 낮다는 기계공학과, 전기공학과, 컴퓨터공학과가 모여있는 건물이었다. 산 꼭대기에 위치하고 있어서 다른과 학생들이 드나들 일도 없을 뿐더러 모든 학과 수업이 이 건물에서만 열리니, 수업이 끝나고 사방에서 체크무늬 셔츠에 두꺼운 안경을 낀 공돌이들이 쏟아져 나오는 광경은 가히 진풍경이라고 할만하다.

japan_eng_student_are_on_check_shirts
단정하게 교복을 입은 공대생들. 체크무늬 셔츠와 두꺼운 안경은 공대생들의 교복이다.

누군가 그랬다. 세상의 절반은 남자고 다른 절반은 여자라고. 그런데 도대체 무엇이 잘못된걸까? 고등학교-대학교-병역특례에 이르는 지난 10년 기간 동안 단 한번도 여자가 많은 조직을 경험해 본 적이 없다. 혹시 내가 뭔가를 잘못하고 있는 것은 아닐까? 세상에 남자와 여자의 수가 같은 집단이라는 게 존재하기는 하는걸까?

성비 균형의 꿈

it_did_happpen

놀랍게도 레저큐에 입사 하면서 나는 그런 조직을 실제로 볼 수 있게 되었다. 여성 직원과 남성 직원의 수가 거의 1대 1인 조직이 실존하고 있었던 것이다.

사실 이런 성비가 가능한 것은 레저큐의 직원들이 맡고 있는 역할과 업무가 정말 폭넓기 때문이다. 개발에서부터 디자인, 상품 운영, CS, 마케팅.. 업무의 폭이 넓으니 직원들의 연령, 성별도 다양할 수 밖에 없다. 그런데 이렇게 1대 1로 균형 잡힌 구성원들의 성비는 남성, 여성 모두에게 어필 할 수 있는 상품들을 판매하는 이커머스 서비스에는 매우 중요한 요소이기도 하다.

남성과 여성은 세상을 인지하고 바라보는 관점, 관심사, 구매 성향이 다르다. 남성 소비자가 서비스에 요구하는 것과 여성 소비자가 서비스에 요구하는 것은 크게 다를 수 있다. 서비스를 운영하는 사람들은 남성 소비자와 여성 소비자 모두를 잡기 위해서 두 관점, 모두에서 서비스를 바라볼 수 있어야 한다. 이를 달성하기 위한 가장 좋은 방법은 당연 구성원에 비슷한 수의 남성과 여성을 두는 것이다.

여성과 남성의 관점 차이에서 생기는 토론과 합의는 더 훌륭한 아이디어를 만들어 낼 수 있는 힘의 원천이 되기도 한다. 서로 다른 관점에서 나온 의견이 때로는 충돌하기도 하고, 때로는 보완되기도 하면서 더욱더 멋진 아이디어가 탄생한다.

실리콘 밸리의 기업들이 성별, 인종, 문화, 전문분야 등에서 다양성을 지향하는 이유도 여기에 있다. 국제적인 서비스, 전 세계 사람들이 이용하는 제품을 만들고 운영하기 위해서는 먼저 서비스를 이용하는 사람들에 대해서 잘 이해하고 있어야 한다. 더불어, 빠르게 변하는 시장에서 끊임 없이 새롭고 멋진 아이디어를 내며 진화할 수 있어야한다. 이를 달성할 수 있는 가장 효과적인 방법은? 바로 기업을 구성하는 구성원의 다양성을 추구하는 것이다.

apple_diversity
애플은 다양성을 모토로 내걸고 있는 회사 중 하나이다. 다양한 인종, 성별, 전공, 문화를 가진 사람들이 애플에서 일하고 있다.

다양성이 비효율을 수반하는가?

어떤 사람들은 이렇게 주장한다. “다양성은 응당 비용을 수반한다. 남성 사원들로 조직된 조직을 상상해보자 이들이 여성을 채용하기 위해서는 기업의 환경, 인프라, 문화를 바꿔야 한다. 이런 비용을 지불하느니 기존에 해왔듯이 남성들로만 기업을 운영하는 것이 더 나을 수도 있다. 그 반대의 경우도 마찬가지이다.”

이들의 말이 맞을 수도 있다. 하지만 레저큐에서 인턴을 하면서 배우게 된 중요한 사실 중 하나는 기업이 성적, 문화적 다양성을 추구하는데 드는 비용보다 그 것을 달성 했을 때 얻을 수 있는 이득이 훨씬 크다는 것이었다.

뿐만 아니다. 다양성의 추구는 기업의 사회적 역할과도 무관하지 않다. 다양한 인종, 그리고 성별, 특히 소수거나 사회적 약자의 위치에 있는 이들에게 기업이 일자리를 제공하고 소득을 분배할 수 있다면 그 어떤 국가적 차원의 지원과 캠페인들이 만들어 내는 것보다 더 큰 효과를 낼 수 있다.

the_more_women_work_the_more_babied_are_born
다양성과 양성평등은 한 커뮤니티, 나아가서 한 사회가 안고있는 골치아픈 문제들을 해결하는 열쇠가 될 수도 있다. (이미지 출처: TechInsider – Norway solved the gender problem that’s creating a crisis in Japan and Korea)

스웨덴의 저명한 통계학자인 한스 로슬링 카롤린스카는 한국을 괴롭히고 있는 저출산과 고령화의 해결책이 페미니즘이 될 수 있다고 주장한다. 실제로 스웨덴은 저출산 문제를 양성평등 확대를 통해서 해결하려는 노력을 펼쳐왔고 이런 문화를 안정적으로 정착시켜 저출산이라는 난제를 멋지게 해결하고 있다.

우리 사회 구성원들의 머리를 아프게 하는 심각한 사회적 문제들을 극적으로 해결할 수 있는 방법이 있다면, 그리고 그 것을 해결하는 것이 아주 사소한 변화에서부터 출발하는 것이라면 이런 문화를 만드는데 투자해야하는 비용은 기꺼이 지불할 만한 것일테다.

옥의 티 찾아내기

우울했던 14대 1 성비의 내 학부시절과 비교했을 때, 레저큐에서의 생활은 분명 훨씬 더 ‘다양’하고 ‘생동감’ 있었다. 이런 점에서 분명 레저큐는 꽤나 모범적인 조직이라고 할 수 있을 것이다. 하지만 여전히 아쉬운 점이 있다. 팀이나 부서별로 떼어놓고 보면 팀을 구성하는 여성, 혹은 남성의 비율이 다소 편중되어 있다는 것이다. 이를테면 레저큐의 개발팀의 개발자 전원은 남성이며, 레저큐의 디자이너들은 한명을 제외하고는 모두 여성이다.

사실 이런 모습은 우리가 소속되어 있는 한국 사회와 정서의 문제와 무관하지 않다. 왜 이공계에는 남성이 압도적으로 많은 것일까. 왜 디자인 학부에는 여학생이 남학생보다 훨씬 더 많은것일까? 정말 남성이 이성적이고 논리적인 사고에 강하고 여성이 섬세하고 감성적인 사고에 강하기 때문일까?

marie_curie
남성은 여성보다 이성적인 사고에 강하며 여성은 남성보다 감성적인 사고에 강하다?

남성과 여성의 능력의 차이에 대해서 생각해보기 이전에 우리 사회가 성역할의 관념에 매몰되어 그런 차이를 만들어내고 있지는 않은지, 혹시 그러한 문제로 인해서 우수한 인재들을 놓치고 더 멋진 기회들을 놓치고 있지는 않은지 되돌아 볼 필요가 있다. 결국 관념과 편견, 문화 그리고 사회적 시스템은 전부 ‘사람’들이 만들기 나름인 것이니까.

여자 개발자, 남자 디자이너를 위하여

레저큐에서 인턴을 하는 동안, 서로 다른 업무를 맡고 있는, 다양한 배경과 성별을 가진 직원들과 소통하며 이 것이 소비자 시장을 대상으로 하는 기업에 강력한 무기가 될 수 있다는 것을 실감했다. 특히 스타트업은 개개인의 개성, 성별, 배경, 그리고 나아가서 문화와 인종의 다양성을 추구할 수 있어야한다.

아쉽게도 다양성의 측면에 있어서 우리는 아직 갈 길이 멀다. 하지만 긍정적인 변화들이 곳곳에서 일어나고 있다. 때로는 이를 추구하고 수많은 담론들을 테이블에 올려놓는 과정에서 수많은 파열음들이 들리기도 한다. 그럼에도 불구하고, 건설적인 담론들이 테이블 위로 올라오고 있다는 것, 사회 구성원들의 생각도 점점 변화하고 있다는 것은 분명 긍정적인 신호일 것이다.

가까운 미래에 여성개발자들과 남성개발자들의 수가 비슷한 개발팀, 그리고 남성디자이너들과 여성디자이너의 수가 비슷한 디자인 팀을 레저큐에서 볼 수 있기를 희망해본다. 어쩌면 기대보다 좀 더 많은 시간이 걸릴 수도 있겠지만 좀 더 다양한 사람들이 일하는 회사, 레저큐는 분명 더 멋지고 생동감 있는 기업이 되어 있을 것이다. 🙂

By EastskyKang

개인취향 JPA 사용기 3편 – Scalable Service

안녕하세요? gemini 입니다.

제가 속한 가자고 팀은 매 2주간의 스프린트 마다 추가 기능을 배포하고 있습니다.
아직 오래전 급하게 만든 코드 등 숙제들이 많지만, 많은 부분이 변하여 순조롭게 기능 확장 중입니다.

이번에 제가 공유하려는 내용은 서비스의 기능을 지속적으로 확장시키는 경험 속에서 느꼈던 점을 공유해보겠습니다.
특히 이번 글은 더 개인 취향에 의한 주관적 생각이오니 많은 의견 주시면 좋을 것 같습니다.

먼저 간단한 상황을 예제로 들어보겠습니다.

기획팀에서 아래와 같은 기능 개발 요청이 왔습니다.
1. 유저는 장바구니에 새로운 상품을 추가할 수 있어야 한다.
2. 유저가 장바구니에 담았던 누적 수량, 금액을 저장하고 있어야 한다.

자, 요구 사항을 위해 Business Logic 을 담을 CartService 를 만들어 보겠습니다.

벌써 다 만들었습니다! Cart 추가할 때에 User 를 불러와서 수량 과 금액 을 갱신 시켜 줍니다.

물론 동작에는 이상이 없지만 CartService 에서 User 를 가져와서 제어하는 형태가 제 취향은 아닌 것 같습니다.
테스트 코드 작성하기도 조금 까다로워 보이네요.

일단 제 취향대로 변경하기 위해 User 의 대한 처리를 UserService 가 수행하도록 변경해보겠습니다.

오우, 좋은 것 같습니다. 테스트 코드도 훨씬 작성하기 쉽겠네요
CartService 는 장바구니 저장만 해주고, User 의 대한 내용은 UserService 에게 맡깁니다.

그런데.. 또 다른 요구 사항으로 인해 UserService 에서도 CartService 가 필요한 상황이 발생합니다.
요구 사항을 반영하기 위해 UserService 에서 CartService 를 사용하여 작업을 진행해봅니다.

엇! 구현을 모두 끝내고 서버를 띄워보니 에러가 발생합니다.
Screen Shot 2016-09-19 at 3.02.57 AM
다들 추측하셨겠지만 UserSerivceCartService 가 상호 참조를 하고 있습니다.
이 문제는 다양한 방법으로 해결할 수 있습니다. 일단은 CartService 쪽 의 의존성을 끊어서 해결하겠습니다.

결국은 CartServiceUserRepository 를 통하여 직접 User 를 가져오고 이를 처리하게 되었습니다.
정상 동작할 것이고, 문제 될 것은 당연히 없습니다.

그렇지만 역시 제 취향은 아니네요.
저의 취향대로 Business Object 를 만들어 핵심 로직을 처리하도록 수정해보겠습니다.

이제 CartServiceUserInfoUpdater 를 사용하도록 수정하겠습니다.

이제 CartServiceUserService 와 관계가 전혀 없어졌습니다. 그리고 CartService 에서는 User 에 관하여 어떤 기능만을 사용하는지 더욱 명확해진 것 같네요.

또한 아주 약한 결합을 가지고 있기에 테스트를 작성하기가 아주 편하고 테스트도 설명하는 바가 명확합니다.

제 취향을 강조하기 위해 빡빡한 예를 들어보았습니다. 적절한 예제였는지는 잘 모르겠습니다, 조금 억지가 있어도 양해 부탁드립니다.

꾸준한 기능 확장으로 인해 Service 들에는 수십 개의 Method 가 생길지 모릅니다.
그런 상황에서 Service 들끼리 엉켜 있다면 서로를 어떤 의도로 쓰는지, 또 다른 곳에서는 어떻게 쓰이는지 예측하기가 힘들어지기 때문에 수정하기 힘들어집니다.

심한 경우에는 AService 안에서 B, C, D, E, F, G, H, I, J 서비스들을 가지고 있을 수도 있겠지요.

결론적으로 제가 공유하고 싶었던 내용은 초기에 Service 를 만들고 기능을 계속 확장해가며 너무 커져버린 Service 로 인하여 생기는 문제, 그로 인하여 점점 더 확장해나가기 힘들어지는 상황에 대한 경험 그리고 그것을 개선하기 위한 고민과 개선해본 경험을 공유하고 싶었습니다.

한 Service 가 많은 의존성을 가지고 있고 많은 일을 하고 있다면, 그 Service 가 너무 많은 책임을 지고 있다는 것입니다.

의존성과 일이 많으면 테스트하기가 까다롭고, 테스트하기가 까다로우면 확장하는데 자유롭지 못 합니다.

끝없이 요구되는 추가 기능과 버그 수정을 감당하며 문제없이 배포하기 위해서는
모든 객체들의 역할이 명확하고 테스트하기 쉬운 구조를 만들어서 객체들이 유연한 형태라면
당연하게도 확장에 유리할 것입니다.

부족한글읽어주셔서 감사합니다. 많은 생각들을 공유해주시면 감사하겠습니다. geminikims@gmail.com

AWS EC2에 엘라스틱서치 클러스터 세팅하기

가자고 개발팀은 판매 데이터 분석에 엘라스틱서치 ElasticSearch 사용을 검토하고 있다. 현재는 테스트 목적으로 AWS 서울 리전(region)에 운영되고 있는 두개 EC2 인스턴스에 각각 엘라스틱서치 노드 1개씩 세팅하고 이들(2개 노드)을 클러스터로 묶어서 운영하고 있다.

AWS EC2에 엘라스틱 서치 클러스터 세팅하기

엘라스틱 서치(이하 ES)는 브로드캐스트 방식(같은 대역의 네트워크 주소를 가진 호스트들에게 패킷을 전송하는 방식)을 이용해서 같은 대역의 네트워크에 내에 있는 다른 ES 노드들을 찾아낸다. 브로드캐스트 기능을 이용하면 같은 클러스터 이름을 가지고 있는 ES 노드들이 저절로 클러스터로 묶여 데이터를 분산 처리할 수 있다.

그러나 EC2 인스턴스에서 브로드캐스트가 막혀있기 때문에 클러스터링을 하기 위해선 직접 모든 인스턴스들을 지정해주거나(유니캐스트 unicast 라고 부른다.) 아니면 AWS Cloud 플러그인을 이용해야한다.

이 포스팅에서는 AWS 플러그인을 이용해서 클러스터링 하는 법에 대해 다룰 것이다.

구동 환경 및 버전 정보

  • AWS EC2 인스턴스
    • 1 vCPUs, 2.5 GHz, Intel Xeon Family, 1 GiB memory (t2.micro)
    • Amazon Linux OS
  • ElasticSearch
    • Version 2.3.5

EC2 인스턴스 설정

EC2 인스턴스에 엘라스틱 서치 클러스터를 설정할 때는 다음의 사항이 반드시 지켜져야 한다.

참고: 인스턴스를 하나에 먼저 ES를 세팅해놓고 이미지를 만든 후에 인스턴스를 복제하는 방식으로 다수의 AWS 인스턴스를 쉽게 만들 수 있다.

ES 설치하기

많은 리눅스 패키지가 그렇듯이 ES도 두가지 방식으로 배포가 된다. 한가지는 yum 이나 apt-get 등의 패키지 매니지먼트 시스템을 이용한 방식이고 한가지는 엘라스틱 서치 웹페이지을 통한 압축파일 배포 방식이다.
장단이 있지만, 고급 사용자가 아니라면 패키지 매니지먼트 시스템을 이용한 설치를 권장하며, 이하에서는 ?? 인스턴스 기준으로, yum 으로 설치한 상황을 가정할 것이다. apt-get(우분투)을 사용하는 경우도 크게 다르지 않다.

yum 을 이용해서 설치하기 위해선 ES 리포지토리 등록이 필요하다. 자세한 방법은 Elasticsearch Reference » Setup » Repositories의 YUM 항목을 참조하면 된다.

설치 후에 중요한 파일들의 경로들은 다음과 같다.

참고로 패키지 매니지먼트 시스템을 이용한 설치를 권장하는 이유는 다음과 같다.

  • 패키지 관리의 용이함 : 설치, 업데이트, 삭제 등이 쉬움.
  • 운영의 용이함 : 환경 변수, service 등의 등록을 별도로 하지 않아도 됨.
  • 실행파일, 설정 파일, 로그 파일 등의 표준 경로로 설정됨.

시작 프로세스 등록 (optional)

ES를 AWS 인스턴스가 새로 부팅되었을 때마다 자동으로 시작되게 할 수 있다. 참고로 yum이나 apt-get 으로 설치를 한 경우에는 /etc/init.d/ 에 elasticsearch 라는 이름의 스크립트가 생성되므로

명령만 실행하면 바로 시작 프로세스로 등록이 완료된다.

압축파일을 다운받아서 설치하는 경우에는 /etc/init.d 경로에 elasticsearch 스크립트를 직접 만들어주어야 한다. 이에 대한 내용은 참조.

플러그인 설치하기

ES 플러그인 설치는 plugin install 명령어를 통해 이뤄진다. ES 플러그인 설치시 실행하는 bin 파일은 yum 으로 설치한 경우 /usr/share/elasticsearch/bin 에 위치하고 있다.

일례로 elasticsearch-head, elasticsearch-HQ, cloud-aws 플러그인을 설치하는 경우엔 /usr/share/elasticsearch/bin 디렉토리 내에서 다음과 같이 명령한다.

참고로 위의 플러그인들 중에 cloud-aws 는 EC2 클러스터링을 위해서 반드시 설치해야 한다.

ES 설정하기

AWS ES 클러스터링의 핵심은 설정파일(elasticsearch.yml)에 있다. 클러스터링을 하기 위해서는 설정파일에서 다음의 조건들이 지켜져야 한다.

  • 같은 클러스터로 묶이는 인스턴스들은 cluster.name 이 모두 같은 이름으로 설정되어야 한다.
  • node.name 은 각 인스턴스를 구분할 수 있도록 지어준다.
  • network.host 는 각 ES 인스턴스가 구동되고 있는 AWS EC2 인스턴스의 private IP 를 적어준다.
  • cloud.aws.region 은 ES 인스턴스의 region
  • discovery 속성은 클러스터링을 위해 같은 클러스터 이름 cluster.name 을 가진 ES 인스턴스들을 검색하기 위한 정보들을 설정해준다. 이 때 검색의 범위는 같은 EC2 region 에 속한 EC2 인스턴스로 제한된다.
    • discovery.typeec2로 입력한다.
    • 만약 현재 해당 AWS region에 구동되고 있는 EC2 인스턴스의 개수가 많다면 discovery.ec2 속성에 다음과 같이 입력해주는 것이 좋다. 이는 클러스터링을 위한 노드 검색의 범위를 같은 security group 을 가진 EC2 인스턴스로 제한한다.
    • discovery.ec2.any_group: false
    • discovery.ec2.groups는 해당 EC2 인스턴스의 security group.

즉 예시로, 가자고 ES 인스턴스의 설정파일은 다음과 같이 작성되어 있다. (private IP와 security group ID는 보안상 주석으로 대신했다.

실행 테스트

yum install 을 했다면 /etc/init.d 에 별도의 스크립트를 등록하지 않아도 service 를 이용해서 ES를 시작할 수 있다.

실행이 정상적으로 됐다면 브라우저에서 EC2 public ip 9200 포트로 접속해보자. 즉 http://<YOUR EC2 PUBLIC IP>:9200로 접속해보자.

위와 같이 뜬다면 정상이다. (<…> 에는 해당 ES 노드의 정보가 뜰 것이다.)

클러스터링이 제대로 됐는지 확인하기 위해서 http://<YOUR EC2 PUBLIC IP>:9200/_cat/nodes?v 로 접속해보자. 아래와 같이 클러스터로 묶인 노드들의 정보가 정상적으로 나오는지 확인해보자.

by EastskyKang

레저큐 인턴기 6편 – 백엔드 개발 도전! (2)

웹API 컨트롤러의 레이어드 아키텍쳐

전편에서 이어집니다…

가자고 웹API의 컨트롤러는

  • Controller 레이어: 가장 상위에서 유저의 리퀘스트(request)를 받고 리스폰스(response)를 돌려줌.
  • Service 레이어: Controller 밑에서 실질적인 비지니스 로직의 구현을 담고 있음.
  • Repository 레이어: DB와의 인터페이스를 담당.

이상의 세개 레이어(layer)로 구성되어 있다. 이렇게 하나의 시스템을 여러개의 레이어(층)로 분리하고 차곡 차곡 쌓아 올라가는 식으로 개발하는 방식을 ‘레이어드 디자인 패턴(Layered Design Pattern)’이라고 부르는데, 소프트웨어 엔지니어링에서 가장 보편적인 디자인 패턴 중 하나이다.

여러가지 이유가 있겠지만 이렇게 레이어를 나누는 가장 중요한 이유는

  1. 규모가 큰 시스템을 여러개로 모듈화하여 한 모듈의 문제가 시스템 전체로 번져나가는 것을 막음과 동시에 각 모듈을 쉽게 교체하기 위함이자, (종속성의 최소화, 재사용성)
  2. 오로지 하나의 이슈를 해결하도록 분리된 각 층을 추상화함으로써 컴포넌트 간에 인터페이스를 쉽게 하고 개발을 용이하게 하기 위함이다. (표준의 지원)

Screenshot 2016-09-06 14.59.49.png
가자고 웹API의 컨트롤러는 Controller, Service, Repository 등의 레이어로 구성된 레이어드 아키텍쳐(Layered Architecture)를 따른다.

레이어드 아키텍쳐 관점에서 개발 미션 바라보기

이쯤에서 나의 개발 미션이 무엇이었는지 다시 떠올려보자.

입력한 조건에 따라 DB에서 서비스 운영에 필요한 통계자료를 추출하는 웹 API를 새로 만들기

개발 미션을 가자고 웹API 컨트롤러의 레이어드 스트럭쳐 관점에서 바라보면 구현 단계를 가장 밑에서부터 네단계로 쌓아 올라가는 것으로 압축할 수 있다.

  1. Model 클래스 구현: 쿼리로 조회한 데이터를 담을 모델 클래스 만들기.
  2. Repository 구현: 데이터 추출 SQL 쿼리를 자바로 구현하기.
  3. Service 구현: 비지니스 로직을 코드로 구현하기. 즉, Repository에서 쿼리로 가져온 데이터들을 어떻게 가공하고 Controller에 다시 돌려줄 것인지 고민하기.
  4. Controller 구현: API에 URL 자원을 RESTFul하게 할당하고 유저의 리퀘스트 처리. 리스폰스 리턴.

사실 Repository 레이어에서 DB로부터 데이터를 가져오는 것을 빼고는 복잡할 것이 없는 작업이다.

  • Model 클래스에서는 내가 필요한 정보(DB의 필드에 해당한다)가 무엇인지 고민하고 이 값들을 담을 수 있는 변수들을 멤버로 가지고 있는 클래스들을 새로 만들어주면 된다. (클래스 만들기는 자바의 기본!)
  • Service에서는 Repository에서 돌려준 데이터들의 묶음(Collector: List, Map 등…)을 엑셀 템플릿과 엑셀 라이브러리를 이용해서 엑셀 파일로 출력해주면 된다. (엑셀 라이브러리만 가져다 쓰면 된다!)
  • Controller는 사실상 스프링 프레임워크에서 제공되는 라이브러리들을 이용하면 자바 어노테이션과 몇 줄 안되는 코드로 구현이 가능하다. (스프링이 알아서 다 해준다…)

자 그렇다면 이번 개발 미션의 핵심이 무엇인지 드러난다. Repository에 SQL SELECT 쿼리를 자바로 구현하는 것이 이번 개발 미션에서 가장 중요한 부분이다.

JOOQ로 SQL SELECT 쿼리를 자바로 구현하기

가자고 어플리케이션은 DB 인터페이스에 JPA(Java Persistence API: 자바 영속성 API)와 JOOQ(Java Object Oriented Querying)를 이용한다.

JPA는 자바 진영의 표준 ORM(Object-relational mapping) 기술로, 관계형 DB를 객체지향패러다임 언어인 자바에서 쓰기 위한 맵핑을 담당하는 기술이다. 표준 영속성(여기서 영속성이란 프로그램이 종료되어도 사라지지 않는 속성, 즉 DB 데이터의 속성을 의미한다) API인 만큼 자바를 이용해서 백엔드 개발을 하는 개발자들이 익히 잘 알고 있는 기술이다.

반면에 JOOQ는 다소 생소한 이름일 수 있다. 스위스 취리히 소재의 스타트업 Data Geekery이 운영하고, 개발하고 있는 JOOQ는, SQL 쿼리를 자바로 구현해야 하는 상황에 이용하는 라이브러리이다. 즉, 미리 작성해 놓은 SQL 쿼리를 그대로 자바로 옮기고 싶다거나, 아니면 JPA로는 구현이 힘든 매우 복잡한 SQL 쿼리를 짜야하는 경우에 멋진 대안이 된다. (참고로 이런 목적으로 사용하는 또 다른 라이브러리 중에 유명한 것이 iBatis 란 기술이다. 가자고 시스템에도 iBatis를 사용한 적이 있었지만 지금은 대부분 JOOQ로 대체하고 있다.)

좀 더 와닿는 코드 예제로 살펴보자. 백엔드 어플리케이션에서 아래와 같은 SQL 쿼리를 사용할 일이 있다고 가정하자. (코드 출처는 wikipedia.org – Java Object Oriented Querying)

위의 SQL 쿼리는 STATUS가 ‘SOLD OUT’인 BOOK 들의 AUTHOR 정보를 가져오는 테이블이다. JOOQ를 이용해서 자바로 변환하면, 이렇게 짤 수 있다.

JOOQ를 이용하면, SQL 문법과 상당히 유사한 자바 코드로 SQL 쿼리를 작성 할 수 있다. 자 그렇다면 이제 JOOQ를 이용해서 내가 작성해두었던 SQL SELECT 쿼리들을 자바로 옮길 차례다.

keep-calm-and-code-on
나우 잇츠 타임 포 코딩!

하나만 간단히 살펴보자. 가자고 서비스 운영에 필요한 정보 중에 특정 아이템을 구매한 고객들의 이름, 이메일, 전화번호를 알아야 하는 경우가 있었다. 이 리스트를 추출하기 위해서 아래와 같은 SQL SELECT 쿼리를 작성했었다.

이 것을 새로 생성한 Repository 클래스의 public List<FrontUser> getItemUserStat(List<String> itemCode) 메서드에 구현했다.

잠시 코드를 살펴보자. 먼저 해당하는 SQL SELECT 쿼리를 JOOQ를 이용해서 만들고 나서, fecthInto(FrontUser.class) 메서드를 호출했다. 이 메서드는 FrontUser 라는 모델 클래스의 리스트(List<FrontUser>)를 리턴하는 것을 확인 할 수 있다. 따라서 이 메서드에, 조회하고 싶은 아이템들의 itemCode를 List<String> 로 전달하면 해당하는 사용자의 정보를 FrontUser 모델 클래스의 리스트로 얻을 수 있다.

이렇게 JOOQ로 쿼리를 짜면 쉽고 간단하게 쿼리를 만들 수 있다. IDE의 자동완성 기능의 도움을 받는다면 일일이 JOOQ의 문서나 매뉴얼을 뒤져보지 않아도 그 자리에서 바로 SQL 쿼리를 자바로 옮길 수 있다.

이렇게 완성된 Repository 메서드를 이제는 거꾸로 Service에서 이용하고, 마지막으로 Controller에서 Service 메서드를 호출하면 SQL로 DB에 쿼리를 날렸을 때와 동일한 데이터를 자바 객체 묶음(Collector: List, Map 등등)으로 얻을 수 있었다. Controller의 각 메서드에 이제 RESTFul 한 URL을 할당해주기만 하면 웹 API가 완성된다!

새로운 세계

그렇게 작성한 나의 첫 백엔드 코드를 커밋(Git Commit)하고, 마침내 나의 풀리퀘스트(Pull Request)가 master 브랜치에 머지(merge) 되는 순간, 새로운 세계가 열렸다! 얕게는 매일 SQL 쿼리를 짜고 DB에서 데이터를 긁어오는 반복 노동으로부터의 해방된 것이고, 깊게는 백엔드 개발의 프로세스에 대한 경험을 해보게 된 것이다. 무엇보다 기뻤던 것은 드디어 가자고 프로젝트에 유의미한 기여를 할 수 있게 되었다는 사실이었다.

물론 이 것은 시작일 뿐이었다. 작동하는 웹 API를 만들어보기는 했지만, 웹 어플리케이션이라는 것이 무엇인지, 어떻게 동작하는지 제대로 이해하게 된 것도 아니고, Spring, JOOQ, JPA와 같은 기술들의 문서를 숙지하고 개발한 것도 아니고, 더군다나 효율적인 개발 프로세스대로 개발을 해본 것도 아니다. 그렇지만 내가 만든 API가 제대로 가자고 웹 어플리케이션 위에서 동작한 것을 확인했을 때의 즐거움이란 어마어마한 것이었다. 하하! 드디어 나도 이제 웹개발자구나! 🙂

여튼 이렇게 웹 개발의 ‘ㅇ’도 모르고 있었던 내가 DB를 다루고 관리하는 일부터 시작해서 백엔드 어플리케이션을 개발하는 백엔드 개발자의 업무를 경험해보기 까지 1달이 좀 못되는 시간이 걸렸다. 물론 그때는 몰랐다. 백엔드 개발에 더 익숙해지는데까지는 훨씬 더 많은 시간이 걸릴 것이라는 것을…

백엔드 개발 도전! 편 끝. 레저큐 인턴기는 계속 이어집니다…
By EastskyKang

개인취향 JPA 사용기 2편 – Entity with Getter,Setter and Test

안녕하세요? gemini 입니다.

지난 1편에서는 기본적인 Spring Boot + Gradle + JPA 설정을 해보고, 기본적인 Entity 를 생성해봤습니다.

오늘은 Entity 의 Getter, Setter 그리고 Test 에 대한 제 개인적인 견해와 활용방법을 공유하려 합니다.

우선 1편에서 다룬 User Entity 를 보겠습니다.

name, phone 필드는 Getter, Setter 를 모두 가지고 있습니다만, id 필드는 Getter 만 존재합니다.

Setter 를 만들지 않은 이유는 id 필드의 수동적 제어를 막기 위함입니다. 애초에 id 필드를 제어 불가로 만들어 혹시 모르는 실수를 막는 것입니다.

또한 기존 코드는 lombok 을 사용하지 않았습니다만.
제 취향대로 User Entity 에 iombok 을 적용해보겠습니다.

우선 저는 다른 기능을 사용하지 않고 @Getter Annotation 만 사용하였습니다,
또한 User Entity 에서 Setter 를 모두 제거하였습니다.

Setter 를 삭제한 이유는 이렇습니다. JPA 는 Transaction 안에서 Entity 의 변경사항을 감지히여 UPDATE SQL을 생성합니다.

즉 Setter 가 Update 기능을 수행하고 있습니다. 이런 상황에서 무분별하게 Setter 를 쓰게 된다면,
예측하지 못한 필드가 Update 가 될 때 Setter 를 일일이 체크해야 합니다.

그럼 Setter 없이 어떻게 데이터 수정을 할까요? 수정 된 User Entity 를 보겠습니다.

updateInfo Method 를 통해서 수정이 가능해졌습니다 어떻게 보면 Setter 가 더 유연해 보일 수도 있습니다. name 만 수정하고 싶을 수도 있기 때문이죠

모든 Entity Method 를 기능화 시키긴 힘듭니다. 그렇지만 생각 없이 @Setter 를 달고 무분별하게 Setter를 사용하는 것은 지양하는 게 좋다고 생각합니다.

이제 앞으로 추가될 모든 Entity의 Primary Key 를 id 필드로 제한하고 싶습니다.

여러 이유가 있지만 기본적으론 PK 를 변경해야 하는 상황이 왔을 때 유연하게 대처하고 싶기 때문입니다.

JPA 는 @MappedSuperclass Annotation으로 Parent Entity Class 를 지원합니다.

그럼, 모든 Entity 의 부모가 될 BaseEntity 를 작성하겠습니다.

추가적으로 모든 Entity 의 생성시간, 수정시간을 기록 할 수 있게 @PrePersist, @PreUpdate Annotation 을 사용했습니다.

이제 User Entity 가 BaseEntity 를 상속받게 구현하겠습니다.

변경된 User 입니다. 자식 Entity 쪽에서는 id 필드가 사라졌으나.

여전히 Id 는 외부 수정이 불가능하고 오직 Persist 시에만 생성됩니다.

다른 팀원들이 BaseEntity를 상속받는 규칙을 지키면 모든 Entity 는 id 필드를 Primary Key 를 가진 형태가 될 것입니다.

자 그럼 조금 더 제 취향에 맞게 변경되었으니, User Entity에 대하여 단위 테스트를 작성해봅시다.

정상적으로 기능이 동작하는 것을 볼 수 있습니다.

위의 테스트는 단순하지만 연관관계가 엮인테스트 또는 id 필드가 반드시 있도록 mocking 해야 하는데
id 필드를 어떻게 설정할 것인가? 이제 와서 Setter 를 추가할 것인가? 아니면 테스트를 하지 않을 것인가?

그럼 이제 test Module 에 아래와 같은 클래스를 만듭니다.

지저분 해 보이기도 하고 그냥 Setter 만들고 싶어질 수도 있습니다. 자 그래도 만들었으니 사용해보겠습니다.

이제 테스트 시에만 id 가 존재하는 Entity 생성이 가능합니다.

저의 경우는 테스트 코드 작성 시 Builder 를 만들어서 사용하는 것을 지향하기에 Builder 내부에서 MockEntity 을 사용할 것 같습니다.

굳이 이렇게까지 id 의 Setter 를 제한할 필요는 없을 수도 있습니다. Setter 쓰는 곳 찾는 게 어렵진 않습니다.

그럼에도 제 생각엔 객체의 행위들은 의미가 있어야 하고, 낭비적 코드를 줄여야합니다.
코드를 간결하고 유의미하게 만드는 것이 가장 중요하다고 생각합니다.

어떤 객체는 모든 필드에 대한 Setter 가 존재해야 할 수도 있습니다.
중요한 것은 무작정 @Setter Annotation 부터 선언하지 않는 것이라고 생각합니다.

두서 없는글 읽어주셔서 감사합니다. 피드백은 언제나 환영입니다. geminikims@gmail.com

**해당 프로젝트 소스는 공유 예정입니다.

– 개인취향 JPA 사용기 1편

[hotjar] Questions-love-to-ask-users 일부번역

https://www.hotjar.com/blog/2016/03/05/questions-love-to-ask-users/#E-commerce

  • 리딩 포인트 : 드라이버, 배리어, 훅 중 무엇을 얻고자 하는 질문인지 생각해보자.

E-commerce
구매전
– 어떤 정보가 누락되었는가? 혹은 어떤 정보가 구입 결심을 쉽게 만드는가?
– 이 아이템을 구입할 때 무엇이 가장 큰 불안(fear)과 우려(concern)인가?
– 오늘의 방문 목적을 달성(complete)할 수 있었는가?
– 만약 오늘 구입을 하지 않았다면, 무엇이 당신을 포기(stop)하게 만들었는가?

구매후
– 결제(checkout) 프로세스에서 개선해야 할 곳은 어디인가?
– 우리 사이트에서 상품을 구입하는데 가장 큰 불안과 우려는 무엇인가?
– 아이템을 구입하도록 유혹한(persuade) 요소는 무엇인것 같은가?
– 만약 더 이상 서비스를 사용할 수 없게 된다면, 가장 아쉬울 것 같은 기능 하나는 무엇인가?

그 외 좋은(great) 질문들
– 가자고를 지인들에게 추천할 확률은 얼마나 되는가? (NPS 질문, 상세내용 => http://book.naver.com/bookdb/book_detail.nhn?bid=7851521)
– 가자고는 몇점(1 – 10)?