<보험용어>

*  생명보험의 정의

   일정한 우발적 사건에 기인하여 발생하는 경제불안에 대비하기 위한 단체적

   경제준비의 한 형태로서 다수주체가 결합해서 확률계산에  의거하여 갹출을 부담하는

   사회적 경제시설

영업보험료 = 순보험료(위험보험료,저축보험료) + 부가보험료(신계약비,유지비,수금비)

*  경험생명표

    경험생명표는 특정한 범위의 생명보험 가입자를 관찰하여 작성된 생명보험회사의 사

    망률에 관한 기록이다.

*  예정이율

   보험회사가 보증하는 최소 이자율

* 역선택

  보험사고의 발생가능성이 높은 계약자가 스스로 보험에 가입하려고 하는 경향

* 잉여금의 이원

                -이차 : 자산운용의 실제의 이율이 예정이율과 다르게 됨으로서 생기는 손익

                -비차 : 예정사업비율에 의한 사업비와 실제 사용한 사업비의 차이에 따른 손익

                -사차 : 보험료 계산에 사용한 예정사망율과 실제 사망율과의 차이에 의해서 생기는 손익

* 계약자

  : 보험회사와 계약을 체결하고 보험료 납입의무를 지는 사람

   ▶ 보험 계약을 체결하고 보험료를 내면서 계약내용을 조회하거나 변경할 수 있는 권리가 있음.

* 계약심사

  보험 청약을 원하는 피보험자의 위험을 선택하여 적절한 위험집단을 분류하는 과정

* 피보험자(보험대상자)

  : 보험사고 발생의 대상이 되는 사람  ▶ 보험금을 받을 원인이 되는 사람

* 유망고객

: 가망고객이라고도 하며 보험가입 가능성이 높은 고객을 뜻한다.

* 가계약

: 보험증권이 발행될 때까지의 무보험 상태를 메꾸기 위한 계약을 말한다.

* 건강진단

: 생명보험 의학의 건강진단은 임상의학의 진찰과 비슷하지만 임상의학에서는 별 문제가 되지 않는 것도 때론 중요한 사항이 된다. 예로서 수술을 받지 않고 치유된 위궤양은 병이 나은 후 수년간은 재발률이 높기 때문에 질병특약부보험의 건강진단시 문제가 되기 때문이다. 또 임상의학에서는 치료대상이 안되는 비만체, 경미한 고혈압 등도 장기간 통계에 의하면 사망률이 높기 때문에 문제가 되는 것이다. 건강진단은 문진부분과 검진부분으로 이루어진다. 문진에 관한 내용은 건강진단의가 피보험자의 성명, 생년월일, 성별, 현재병증, 과거병력, 직업, 약물의 상용 등에 대해서 청취하는 것을 말한다. 검진은 보통 내과적 진찰 외에 신장, 체중, 가슴둘레, 배둘레 등의 측정을 하게 되는데 필요에 따라서 심전도검사, 안저검사, 간기능검사 등도 실시한다. 진단의는 보험 회사의 직원인 사의와 보험회사가 진단을 위촉한 촉탁의가 있다.

* 기납입보험료

: 보험증권이 발행될 때까지의 무보험 상태를 메꾸기 위한 계약을 말한다.

* 단체보험

: 많은 사람의 집단을 대상으로 하여 원칙적으로 무진단의 1매의 보험증권에 의하여 일괄하여 체결하는 생명보험으로 일반적으로 단체생명보험을 의미. 단체를 선택의 단위로 하기 때문에 보험회사가 위험선택, 보험료 수금의 수고를 생략할 수 있어 보험료 책정을 저렴하게 할 수 있는 장점이 있다

* 피보험자

: 생명보험계약에 있어서는 사람의 생과 사라는 보험사고발생의 객체가 되는 사람을 피보험자라고 한다.

* 보험수익자(보험금을 수령하는 자)

  :  보험사고 발생 시 보험금 청구권을 갖는 사람

   ▶ 일반적으로 생존시에는 피보험자/사망시는 법정상속인으로 수익자 지정됨.

* 보험금

 : 피보험자가 사망,장해,입원,만기 등 보험금 지급사유가 발생하였을 때 보험회사가 보험수익자에게

   지급하는 금액

* 보험료

  : 보험금을 받기 위해서 보험계약자가 매월 또는 지정한 기간에 납입하는 돈

▶ 기본보험료 : 계약자가 매 납입기일에 납입하기로 한 보험료로 보장보험료와 적립보험료의 합계액

▶ 보장보험료 :  보험계약에 따른 보장을 받기 위하여 보험계약자가 보험회사에 납입하는 보험료

▶ 적립보험료 :  회사가 적립한 금액을 돌려주는데 필요한 보험료

▶ 적립부분 순 보험료 :  적립보험료에서 사업비를 공제한 후의 금액

* 책임준비금

 : 장래의 보험금, 해약환급금 등의 지급을 위하여 보험계약자가 납입한 보험료 중 일정액을

   보험회사가 적립해 둔 금액

* 해약환급금(해지환급금)

 : 계약의 효력상실 또는 해지시 보험계약자에게 돌려주는 금액

* 보험기간

 :  보험계약에 따라 보장받는 기간

* 납입기간

: 보험료를 납입하는 기간

* 만기

 : 정해진 보험기간이 다 경과하여 끝난 시점

* 보장개시일 (책임개시일)

 :  보험회사의 보험금 지급의무가 시작 되는 날 = 보험효력이 발생하는 날

* 청약철회

 :  보험계약자가 계약 후 일정기간 내에 당해계약의 취소를 요구할 수 있는 제도

* 품질보증

 : 보험계약 체결 후 3개월 이내에 보험사의 "보험판매 3대의무" 위반 시 보험계약을 취소할 수 있는

   제도 (3대의무 : 1> 청약서 자필서명 2> 약관 및 청약서 부본전달 3> 상품의 주요내용 설명)

* 실효

 : 보험료가 유예기간까지 납입되지 않아 효력이 없어져 보험사고가 발생해도 보상을 받지 못하는 상태

   ▶ 보험료를 2회 미납시 익월 1일부터 효력이 없어짐

     ex) 2010.9월 계약시 1회 보험료 납입 후 10월보험료, 11월 보험료 미납

          → 12월 1일자로 실효상태

* 부활

 : 해지환급금을 받지 않은 상태에서 실효일로부터 2년이내에(만기일 이전) 계약의 효력을 다시 발생

   시키는 것을 말함

* 소멸

: 소멸은 보험계약자와 보험회사 간에 체결되었던 보험계약에 따른 제 권리 및 의무관계가

  종료되는 것을 말하며, 보험계약에 있어서 소멸사유는 보험사고발생, 해약, 효력상실, 만료,

  무효

* 고지의무

 :  보험계약자 또는 피보험자가 보험계약을 체결함에 있어 중요한 사실을 알리고, 중요한 사실에

    관하여 부실한 사실을 알리지 아니할 의무

 ▶  보험계약 체결 시 병력,직업,운전여부 등 피보험자에 관한 중요한 사실을 보험회사에 알려야 하는

    의무로 부실하게 알릴 시 차후 계약유지나 보험금 수령에 문제가 발생할 수 있음.

* 부담보 설정 (부보장 설정)

 : 보험계약 체결 시 과거질병이나 사고로 인한 후유증이나 경과관찰이 필요할 시 해당부위의 보장을  제외하고 가입하는 조건

    ex) 1년전 "폐기흉" 수술력 고지 : "폐"부위에 대하여 3년간 부담보 ( 폐부위 3년간 보장 제외)

* 자필서명

 : 보험계약서를 자필로 작성하여 최종적으로 서명 날인하는 절차

* 보험약관

  : 보험계약에 관하여 보험계약자와 보험회사 상호간에 이행해야 할 권리와 의무를 규정한 것.

* 보험증권 (보험가입증서)

 : 보험계약의 성립과 그 내용을 증명하기 위하여 보험회사가 보험계약자에게 교부하는 증서

  ▶ 보험 계약 후 교부되며 가입일자, 보장내용 등 보험계약체결 정보가 들어있음.

* 약관대출(보험계약 대출)

 : 약관대출이란 계약자가 가입한 보험 해약환급금의 70∼80%의 범위에서 수시로 대출받을 수 있는 제도. 상품에 따라서 납입 보험료 대비 약관대출의 범위가 다르다

* 중도인출

 : 계약일 이후 1개월경과후 연금개시전 보험기간중 적립액의 일부 인출 가능.

  계약자가 가입한 보험 해약환급금의 50%의 범위

  대출이 아니므로 이자납입이 필요없음.1년에 4~12번정도 인출가능

 

* 휴면보험금

:보험금의 지급 사유가 발생한 날로부터 2년이 지나 소멸시효가 완성되었지만, 보험 계약자가 찾아가지 않아 보험회사에서 보관하고 있는 돈.

1. 성적의 종류 및 정의

  가. 성적의 종류

          환산월초, 총환산월초, 유효월초

  나. 성적의 정의

         (1) 환산월초

             신계약 실적에 대한 상품별 비교지표를 설정하기 위하여

             월납보험료를 예정신계약비 규모에 비례하여 환산한 보험료

         (2) 총환산월초

             초년도 및 2차년도 환산월초를 합산한 성적

         (3) 유효월초

             당월 모집한 총환산월초에 전월 산출 2~13회 통산 유지율을

             적용한 성적

2. 환산월초

         ※ 저축성 일시납(거치형) 상품은 환산월초 불계상

  가. 계상방법

         초년도와 2차년도로 구분

         (1) 초년도 환산월초는 신계약 성립시 일시 계상

         (2) 일시납은 총환산월초를 초년도 환산월초로 계상

3. 유효월초

  가. 정 의

          당월 모집한 총환산월초에 FP 본인의 전월 2~13회차 통산

          월납환산 보험료 유지율(2연체)을 적용한 성적

          

  나. 산출방법

 

        유효 월초 = 당월 총환산월초  X  전월 2~13회차 통산 유지율

 

         (1) 당월 총환산월초

             체결된 신계약에 의해 계상되는 총환산월초

         (2) 통산 유지율 계상방법

             전월 「2~13회 통산 유지율」적용시

              2회유지 + 3회유지 + 4회유지 … + 13회유지

           -------------------------------------------   X  100%

              2회성립 + 3회성립 + 4회성립 … + 13회성립

             예) 4월 유효월초

                = 4월 총환산월초 × 3월 2~13회차 통산 유지율

 

* 제효율 산출 및 용어기준

1. 정착율

   정착율 =       잔존인원  /   위촉인원    X   100

   ※ 잔존인원 : ① 당월 현재 위촉중인 설계사

                      ② 13차월 정착율 계상시는 자기모집 보유계약이 10건이상

                        이고 당월 신계약 실적이 있는 자를 잔존인원으로 산출

  ※ 무위촉으로 인한 정착율 산출불가시 정착율 “0%”로 계상함

2. 유지율

   유지율 =  유지계약 (액ㆍ보험료ㆍ건수) / 신계약 (액ㆍ보험료ㆍ건수)  X 100

       ※ ① 무실적으로 인한 유지율 산출불가시 유지율 “0%”로 계상함

           ② 월납환산보험료 유지율 산출시 일시납 및 퇴직보험, 보험기간 1년

              상품은 제외함

           ③ 유지율 산출시 소수점 첫째자리 아래는 절사함

3. 수금율

   수금율액  = 실수금애  /  요수금액   X 100

   ※ 요수금액 산출기준

   월납계약  -  당 월 : 1회 보험료 × 100% ,  2연체 : 1회 보험료 × 200%

   비월납계약 - 당월, 2연체 : 1회 보험료 × 100%

         단, ① 선납 및 부활실적은 요수금 및 실수금에 포함

              ② 당월분 입금후 해약계약은 요수금 및 실수금에 포함

4.월납환산초회료(보험료) 산출방법

  가. 2개월납 : 초회료 ÷  2

  나. 3개월납 : 초회료 ÷  3

  다. 6개월납 : 초회료 ÷  6

  라. 연   납 : 초회료 ÷ 12

  마. 일시납

         (1) 교육보험 : 초회료 ÷ 30

         (2) 기타상품

             o 보험기간 2년         : 초회료 ÷ 20

             o 보험기간 3년         : 초회료 ÷ 30

             o 보험기간 4년         : 초회료 ÷ 40

             o 보험기간 5년이상 : 초회료 ÷ 50

  바. 저축성 일시납(거치형) 및 퇴직보험 상품은 불계상

  사. (무)레저보험 : 초회료 ÷ 12

  ※ 기타 신종상품은 상품판매방침에 따름

5. 협약단체

  계약단체중 직장내 계약자수가 5명 이상으로 당사와 단체취급특약

  약관에 의거 단체취급협약을 체결하고 보험료를 계약자(피보험자)의

  급여에서 일괄공제 납입하여 할인보험료를 적용받고 있는 단체

6. 승환계약

                   

가. 대상계약

   기계약의 실효, 해지 판명월 및 전ㆍ후 3개월이내 가입한 모든 계약

나. 계약관련자 : 계약자, 주피보험자

다. 적  용

          (1) 신계약이 승환계약으로 판명시 승환 해당분에 대해서만

             신계약 성적을 50%적용하여 수수료, 경비, 인센티브 등 지급

              ※ 신계약 성적이 기계약 성적보다 클 경우 초과분 성적은 100% 계상

           (2) 2회이후 유지계약이 승환계약으로 판명시

             판명월 이후 신계약수수료 적립분 및 유지서비스수수료 50%지급

           (3) 다음의 경우 승환 대상계약에서 제외함

               ① 만기 또는 24회 초과한 계약 (계약월 기준)

               ② 실효후 해약한 계약 (실효에 따른 승환계약만 적용)

③ 단체상품 및 다이렉트 전용상품, 방카슈랑스상품, 계약전환상품

           예> 신계약 환산월초 100천, 해약된 기계약 환산월초 50천 가정시

                ☞ 신계약 환산월초 중 50천 – 50%로 계상

                   신계약 환산월초 중 50천 – 100%계상 ⇒ 신계약 75% 성적 반영

7. 부당모집추정계약

        가. 대상계약

          모든 계약의 마감전ㆍ후 청약철회, 무효(해지) 처리월 및 전·후

          1개월이내 타 모집자로 재가입되는 신계약

          ※ 무효(해지)계약 산출시 적용되는 계약

             (1) 책임보상으로 인한 경우

             (2) 주피보험자 미동의에 의한 경우

             (3) 대내ㆍ외 민원에 의한 경우

         예> 2008.4월 청약철회, 무효(해지)발생시

                2008.3.1일부터 2008.5.31일까지 재가입되는 신계약(3개월 적용)

        나. 계약관련자 : 계약자, 주피보험자

        다. 적  용

          (1) 신계약이 부당모집추정계약으로 판명시

             타모집자로 재가입되는 신계약에 대하여 성적의 50%만 계상

          (2) 2회이후 유지계약이 부당모집추정계약으로 판명시

             판명월 이후 신계약수수료 적립분 및 유지서비스수수료 50%지급

          ※ 세부적용기준

             ① 단체상품, 방카슈랑스상품, 다이렉트 상품, 계약전환상품은

                 대상계약에서 제외

             ② 동일월 부당모집추정계약이 승환계약과 이중으로 판정시

               승환계약을 우선 적용함

             ③ 부당모집추정계약(승환계약)으로 기판명후 다시 승환계약

                (부당모집추정계약)으로 판명시 先판명된 기준만 적용함

8. 무효 ·해지, 청약철회 계약

     가. 무효 : 계약 당사자가 의욕한 법률 효과가 발생하지 않는

                   것으로 다음에 해당하는 계약

         

          (1) 계약전 알릴의무 위반 계약

          (2) 타인의 사망을 보험금 지급사유로 하는 계약에서

                  계약 체결시까지 보험대상자(피보험자)의 서면에 의한

                  동의를 얻지 아니한 경우

              단, 단체가 규약에 따라 구성원의 전부 또는 일부를

                  보험대상자(피보험자)로 하는 계약을 체결하는

                  경우에는 이를 적용하지 않음

          (3) 만15세 미만자, 심신상실자 또는 심신박약자를

                  보험대상자(피보험자)로 하여 사망을 보험금 지급

                  사유로 한 계약

          (4) 고객 민원 신청에 대하여 회사가 이를 수용함으로써

                  기 납입 보험료를 계약자에게 지급한 계약

      나. 해지 : 계약 당사자 한쪽의 의사표시에 의하여 계약에

                     기초한 법률관계가 소멸 되는 것으로 다음에

                     해당하는 계약

          (1) 계약자에게 약관 및 청약서 부본을 전달하지

                  아니하거나, 약관의 중요한 내용을 설명하지 아니한때

                  또는 계약체결시 계약자가 청약서에 자필서명

                  (날인(도장을 찍음))을 하지 아니한 사유로 청약일부터

                  3개월 이내에 책임보상을 신청한 계약  

          (2) 보험대상자(피보험자)가 고의로 자신을 해쳐 이미

                  납입한 보험료나 해약환급금을 계약자에 지급한 계약

          (3) 보험금을 받는자(보험수익자)가 고의로

                  보험대상자(피보험자)를 해쳐 이미 납입한 보험료나

                  해약환급금을 계약자에 지급한 계약

          (4) 계약자가 고의로 보험대상자(피보험자)를 해쳐 이미

                  납입한 보험료나 해약환급금을 계약자에 지급한 계약

      다. 마감후 청약철회

            매월말 신계약 마감 정산 후 청약을 철회한 계약

블로그 이미지

낭만가을

,

웹 서비스를 운영하는 서버 관리자라면 외부에서 어떤 요청이 들어오는지 그리고 어떤 사용자가 있는지에 대한 정보를 담고 있는 액세스 로그에 관심을 가질 필요가 있습니다. 과거 인터넷이 발달하기 전 시절과는 달리 요즘은 PC 및 스마트폰의 대중화로 다양한 OS와 브라우저를 사용하고 있고, 공개 도메인에 대해 크롤러나 외부 해킹과 같은 무차별적 호출이 많아지고 있는 추세이기 때문입니다.

다행히도 일반적인 웹 서버는 이와 같은 외부 호출에 대한 정보를 로그로 남기는 기능을 제공합니다. 특히 Apache는 다양한 형식으로 액세스 로그를 남길 수 있는 모듈을 제공하기 때문에 이를 활용하면 유의미한 데이터를 만들어 낼 수 있습니다.

이 글에서는 Apache 2.2를 웹 서버로 운영하며 액세스 로그를 분석해 유의미한 결과를 얻은 사례를 공유하겠습니다.

Apache 액세스 로그는 어떻게 생겼을까

Apache 액세스 로그를 분석하기 위해서 어떤 형식으로 구성되어 있는지, 그리고 각 데이터에서 어떤 정보를 추출할 수 있는지 알아보자.

Apache는 log_config_module 모듈을 제공하여, 형식 문자열을 사용하여 액세스 로그의 형식을 설정할 수 있게 한다. 형식 문자열 중 이 글에서 필요한 부분만 살펴보면 다음과 같다.

표 1 Apache 액세스 로그 형식 문자열(출처: 아파치 모듈 mod_log_config)

형식 문자열설명
%%퍼센트 기호
%...bHTTP 헤더를 제외한 전송 바이트 수. CLF 형식과 같이, 전송한 내용이 없는 경우 0 대신 -가 나온다.
%...D요청을 처리하는 데 걸린 시간(마이크로초 단위).
%...h원격 호스트
%...{Foobar}i서버가 수신한 요청에서 Foobar: 헤더의 내용.
%...l(있다면 identd가 제공한) 원격 로그인명. mod_ident가 있고 IdentityCheck가 On이 아니면 빼기 기호를 기록한다.
%...r요청의 첫 번째 줄
%...s상태(status). 내부 리다이렉션된 요청의 경우 원래 요청의 상태이다. 최종 요청의 상태는 %...>s.
%...tcommon log format 시간 형식(표준 영어 형식)의 시간
%...u원격 사용자(auth가 제공하며, 상태(%s)가 401인 경우 이상한 값이 나올 수 있음)

형식 문자열을 사용하여 Apache 설정 파일(httpd.conf)에 다음과 같이 액세스 로그의 형식을 설정할 수 있다.

LogFormat "%h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" combined  
CustomLog "| /~<del>/apache/bin/rotatelogs -l /</del>~/logs/apache/access.log.%Y%m%d 86400" combined env=!nolog-request  

위와 같이 설정한 결과 생성된 액세스 로그는 다음과 같다.

123.123.123.123 - - [12/Apr/2018:17:03:50 +0900] "GET /api/aaaa HTTP/1.1" 200 34 1468 "https://m.naver.com" "Mozilla/5.0 (iPhone; CPU iPhone OS 11_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E216 NAVER(inapp; search; 580; 8.6.3; 7)"  

외부에서 요청이 발생할 때마다 위와 같은 문자열이 로깅된다. 그렇다면 이 문자열은 무슨 의미일까? 간단하게 살펴보면 아래와 같은 정보로 구성되어 있는 것을 확인할 수 있다.

그림 1 액세스 로그 구성 예

그림 1 액세스 로그 구성 예

  1. 원격 호스트 IP 주소(요청자)
  2. 요청 시간
  3. 'GET' 메서드를 사용하고 '/api/aaaa'라는 경로에 'HTTP/1.1'의 프로토콜로 요청
  4. HTTP 상태 코드
  5. HTTP 헤더를 제외한 전송 바이트 수
  6. 요청을 처리하는 데 걸린 시간(ms)
  7. 리퍼러(referrer)

위에서 언급한 LogFormat 내용 중 마지막에 있는 User-Agent(이하 UA)는 Wikipedia에 따르면 ‘사용자를 대신하여 일을 수행하는 소프트웨어 에이전트’라고 한다. 즉, UA만 알아도 어떤 기기/브라우저를 사용하는지 알 수 있다는 것이다. UserAgentString.com에 접속해 보면 자신의 UA 문자열을 파싱하여 얻은 OS, 브라우저 등의 정보를 볼 수 있다. Mozilla MDN의 User-Agent - HTTP에는 다양한 브라우저의 UA 문자열과 지원 정보가 정리되어 있다. 주로 사용되는 브라우저는 모두 UA 문자열을 지원하는 것을 확인할 수 있다.

그림 2 User-Agent 브라우저 호환성

그림 2 User-Agent 브라우저 호환성(원본 출처: http://developer.mozilla.org)

이렇게 Apache가 제공하는 모듈을 사용하면 요청지의 다양한 정보를 확인할 수 있다. 하지만 하루에 생성되는 액세스 로그가 수백 건, 수천 건이라면 한줄 한줄을 살펴보고 있을 수는 없다. 우리는 개발자이니 개발자답게 이를 분석해서 한눈에 볼 수 있는 방법을 생각해 보자.

분석 방법 1: 가장 단순하게

그럼 내가 운영하는 서버에 들어온 사용자 혹은 요청을 어떻게 분석할 수 있을까? 가장 단순한 방법으로는 이러한 데이터를 Linux 명령어나 Excel 등을 활용하여 데이터를 추출한 후 정규식을 사용하여 일정한 형식으로 만들고 그 결과를 다시 그룹화하면 얼추 원하는 정보를 얻을 수 있다. UA 문자열과 같은 경우는 이미 다른 사람들이 만들어 둔 정규식을 가져다 사용할 수도 있겠다(예: RegExr).

하지만 자동화되어 있지 않아 정보를 얻으려 할 때마다 매우 번거롭다. 데이터 추출 자동화를 직접 개발한다고 해도 실시간으로 정보를 살펴보고 싶은 경우에는 제한 사항이 많다.

분석 방법 2: Elastic Stack을 활용한다면

Elastic 공식 홈페이지에서도 소개하듯이 Elastic Stack은 실시간 데이터 분석에서부터 다양한 형식의 시각화까지 제공하고 있다. 오픈소스인 Elastic Stack은 크게 데이터를 담는 Elasticsearch, 데이터를 수집 및 전송하며 파이프라인 역할을 담당하는 Logstash, 그리고 데이터를 시각화하는 Kibana로 구성되어 있다. 전에는 ELK라고도 불렸으나 지금의 정식 명칭은 Elastic Stack이다. 그럼 이 Elastic Stack을 활용하여 어떻게 단순한 방법을 벗어나서 효율적으로 서버로부터의 인입을 분석할 수 있을까?

간단하게 설명하면, 위에서 언급한 액세스 로그를 사용하지 않고 프런트엔드 단계에서 JavaScript로 UA 문자열을 구한다. 이러한 정보를 받을 수 있는 API를 서버에 만들어 두고 서버에 UA 문자열을 전송한다. 서버에서는 해당 UA 문자열을 분석 후 Elasticsearch로 전송한다. 알기 쉽게 그림으로 살펴보자.

그림 3 좀 더 '나은' 방법 Elastic Stack 활용

그림 3 좀 더 '나은' 방법 - Elastic Stack 활용

프런트엔드 단계에서는 navigator.userAgent를 활용하여 UA 문자열을 구할 수 있다. API에서는 UA 문자열을 받아 파싱하는데 관련 코드는 다음과 같이 작성하였다.

private static final String VERSION_SEPARATOR = ".";  
private void userAgentParsingToMap(String userAgent, Map<String, Object> dataMap) {  
    HashMap browser = Browser.lookup(userAgent);
    HashMap os = OS.lookup(userAgent);
    HashMap device = Device.lookup(userAgent);
    dataMap.put("browserName", browser.get("family"));
    dataMap.put("browserVersion", getVersion(browser));
    dataMap.put("osName", os.get("family"));
    dataMap.put("osVersion", getVersion(os));
    dataMap.put("deviceModel", device.get("model"));
    dataMap.put("deviceBrand", device.get("brand"));
}
private String getVersion(HashMap dataMap) {  
    String majorVersion = (String)dataMap.get("major");
    if (StringUtils.isEmpty(majorVersion)) {
        return StringUtils.EMPTY;
    }
    String minorVersion = (String)dataMap.get("minor");
    String pathVersion = (String)dataMap.get("path");
    StringBuffer sb = new StringBuffer();
    sb.append(majorVersion);
    if (!StringUtils.isEmpty(minorVersion)) {
        sb.append(VERSION_SEPARATOR);
        sb.append(minorVersion);
    }
    if (!StringUtils.isEmpty(pathVersion)) {
        sb.append(VERSION_SEPARATOR);
        sb.append(pathVersion);
    }
    return sb.toString();
}

참고로 Java 단계에서 UA 문자열을 파싱하는 파서는 여러 가지가 있는데 그중 uap_clj 모듈이 상세한 파싱 결과를 제공하여 이를 사용했다.

표 2 UA 문자열 파싱 모듈 비교

모듈브라우저OS기기
eu.bitwalker.useragentutils.UserAgentO불명확함(예: Android 5.x)X
net.sf.uadetector.UserAgentStringParserOO불명확함(예: Smartphone)
uap_clj.java.api.*OOO

파싱 결과는 다음과 같다.

Browser : {patch=3239, family=Chrome Mobile, major=63, minor=0}  
OS : {patch=1, patch_minor=, family=Android, major=5, minor=1}  
Device : {model=Nexus 6, family=Nexus 6, brand=Generic_Android}  

위와 같이 구성하면 Elasticsearch에 인덱싱된 데이터를 Kibana에서 입맛에 맞게 실시간으로 볼 수 있다. 하지만 별도의 API를 만들어야 한다는 점, 프런트엔드 단계에서 로깅을 위한 JavaScript 로직이 들어가야 한다는 점, 운영하는 모든 페이지에 해당 JavaScript를 넣어야 한다는 점, 액세스 로그만큼 풍부한 정보를 볼 수 없다는 점 등 여러모로 아쉬운 점이 많다. 뭔가 더 좋은 방법이 없을까? 깔끔하면서도 '우아한' 방법. 더 열심히 생각해 보자.

분석 방법 3: 조금 더 우아하게

Logstash보다 가벼운 log shipper인 Filebeat를 이용하여 Apache 액세스 로그를 수집한다. Filebeat의 설정은 다음과 같다.

filebeat.prospectors:  
  - type: log
    enabled: true
    paths:
      - /log/access.log.*
output.logstash:  
  hosts: ["x.x.x.x:5044"]

이렇게 Filebeat가 액세스 로그를 수집하여 Logstash에 전달하면 UA 문자열을 파싱하는 Logstash의 plugins-filters-useragent와 IP 주소를 정해진 규칙에 의해 위도/경도 값으로 바꿔주는 Logstash의 plugins-filters-geoip를 활용해서 로그를 입맛에 맞게 필터링한다. 설정은 다음과 같다.

input {  
        beat {
                port => "5044"
        }
}
filter {  
        grok {
                match => { "message" => ["%{IPORHOST:clientip} (?:-|%{USER:ident}) (?:-|%{USER:auth}) \[%{HTTPDATE:timestamp}\] \"(?:%{WORD:httpMethod} %{NOTSPACE:uri}(?: HTTP/%{NUMBER:httpversion})?|-)\" %{NUMBER:responseCode} (?:-|%{NUMBER:bytes}) (?:-|%{NUMBER:bytes2})( \"%{DATA:referrer}\")?( \"%{DATA:user-agent}\")?"] }
                remove_field => ["timestamp","@version","path","tags","httpversion","bytes2"]
        }
        useragent {
                source => "user-agent"
        }
        if [os_major]  {
                mutate {
                        add_field => {
                                os_combine => "%{os} %{os_major}.%{os_minor}"
                        }
                }
        } else {
                 mutate {
                        add_field => {
                                os_combine => "%{os}"
                        }
                }
        }
        if [os] =~ "Windows" {
                mutate {
                        update => {
                                "os_name" => "Windows"
                        }
                }
        }
        if [os] =~ "Mac" {
                mutate {
                        update => {
                                "os_name" => "Mac"
                        }
                }
        }
        geoip {
                source => "clientip"
                target => "geoip"
        }
}
output {  
        kafka {
                bootstrap_servers => "~<del>"
                topic_id => "</del>~"
                codec => json{}
        }
}

설정하는 과정에서 grok filter 패턴을 작성하는 데 많은 시간을 할애해야만 했다. 개인적으로는 http://grokconstructor.appspot.com/do/match?example=2에서 테스트해 보면서 패턴을 작성한 것이 도움이 되었다. 그리고 Logstash가 파싱한 정보를 조금 다듬기 위해 mutate filter를 사용해서 필드를 조합, 수정하였다.

마지막으로, 인입량이 많을 경우 버퍼 역할을 하고 데이터 유실을 방지하기 위해 중간에 Kafka를 두었다. 관련 내용은 "아파치 엑세스 로그를 엘라스틱서치에 인덱싱 해보자"에 정리했다. 전체적인 흐름을 그림으로 보자.

그림 4 좀 더 우아한 방법

그림 4 좀 더 우아한(?) 방법

분석 결과를 한눈에

위에서 설정을 이용해서 수집한 액세스 로그가 Elasticsearch에 담겼으니 Kibana를 이용하여 시각적으로 나타내면 분석에 도움이 될 수 있다.

Logstash에 내장된 데이터베이스를 활용하여 IP 주소를 위치(geoip)로 변환해서 지도 및 그래프로 나타냈다. 그리고 URI를 그룹화하여 타입별로 시각화했다. 해외에는 서비스하고 있지 않기 때문에 국내 접속이 월등히 많고 그중 서울에서 접속을 가장 많이 한다는 것을 한눈에 볼 수 있다(해외 접속은 무차별적인 호출 또는 해킹 시도로 보인다).

그림 5 지도 및 그래프로 나타낸 접속 위치

그림 5 지도 및 그래프로 나타낸 접속 위치

또한 UA를 각 타입별로 그룹화할 수도 있다. PC보다 모바일 사용자가 더 많고, 그중 안드로이드 7.0에서 가장 많이 접속하는 것을 확인할 수 있다(이런 정보를 기반으로 비즈니스 모델을 타겟화할 수도 있겠다).

그림 6 그래프로 나타낸 UA 타입별 접속 수

그림 6 그래프로 나타낸 UA 타입별 접속 수

마치며

여러 가지 방법을 사용하여 Apache 액세스 로그를 분석해 보았다. raw 상태의 데이터를 보는 것보다 유의미한 데이터로 가공하여 시각화하는 것이 데이터의 의미를 이해하는 데 도움이 되었다.

또한 결과를 도출하기 위해 백지 상태부터 개발하여 도구를 만드는 것도 좋은 방법일 수 있으나, 이 사례에서 Elastic Stack이라는 오픈소스를 활용했듯이 외부 오픈소스를 활용하는 것도 고려해볼 만하다고 생각한다.

블로그 이미지

낭만가을

,

백엔드 개발자의 진로

일의 범위

백엔드 개발자는 현업에서 실제 무슨 일을 하고 어떤 일을 할 수 있나요?

'백엔드 개발자'가 담당하는 일은 범위가 넓습니다. 사용자에게 보이는 웹 어플리케이션 개발뿐만 아니라 데이터 분석을 위한 엔지니어링, 분산파일시스템이나 DBMS와 같은 제품을 만드는 개발자들도 백엔드 개발자라 불리기도 합니다. '웹 프런트엔드 개발자', '모바일앱(iOS/Android) 개발자' 에 비하면 그 대상이나 하는 일이 상대적으로 모호합니다. 어플리케이션을 개발하는 백엔드 개발자는 프로젝트에 따라서는 서버관리, DB관리, 프런트엔드 개발까지 모두 담당하기도 합니다. 현대 야구가 발전하면서 선발/중간/마무리 투수가 구별된 것처럼 이전에는 백엔드 개발자가 다 하던 일이 분업화된 영역도 있습니다.

이렇듯 백엔드 개발자는 폭넓은 기술을 접할 수 있는 역할을 수행합니다. SE(System engineer), FE(Front End) 등 인접한 분야의 개발자와 소통할 기회가 많습니다. 그리고 접한 분야 중 한 분야에 대한 전문성을 키울 수 있는 기회를 만나기도 쉽습니다. 예를 들면 담당하는 서비스의 통계 모듈 개발로 시작해서 대용량 데이터를 다루는 데이터 엔지니어로 성장하는 경우입니다.

특화된 분야의 전문가로 성장하더라도 어플리케이션을 잘 개발하는 능력은 중요합니다. 다른 개발자가 사용하는 플랫폼이나 라이브러리의 개발도 어플리케이션 개발의 특성을 이해해야 적용하기가 편리한 인터페이스를 설계할 수 있습니다. 대용량 데이터 분석이나 처리를 담당하는 개발자라도 업무 효율화를 위해 모니터링, 관리 도구를 만들어야 할 때도 있습니다. 그래서 깊이 있는 전문분야와 함께 어플리케이션 개발 능력을 갖춘다면 더욱 유능한 개발자가 될 수 있습니다. 남은 질문에 대한 대답들은 백엔드 어플리케이션 개발 분야에 초점을 맞춰서 드리고자 합니다.

보람과 고충

백엔드 개발을 할 때 가장 어려운 점과 백엔드 개발을 하면서 느낀 매력은 무엇인가요?

개발 프로젝트 팀을 음악 밴드에 비유하자면, 인프라 담당자와 백엔드 개발자는 드럼이나 베이스 기타와, 프런트엔드 개발자는 보컬이나 퍼스트 기타와 비슷하게 느껴집니다. 무대에서 주목받고 찬사를 받기 쉬운 쪽은 보컬이지만 베이스와 드럼이 안정적으로 뒷받침하지 못하면 좋은 공연이 나올 수 없습니다.

백엔드 개발자는 시스템을 안정적이고 효율적으로 만들 때 보람을 느낍니다. 사용자가 갑자기 몰려와도 에러 없이 서버 프로그램이 실행될 때, 성능을 획기적으로 개선했을 때, 모듈의 구조를 개선해서 코드를 많이 줄였을 때가 그런 경우입니다. 문제가 생기지 않을 때에는 겉으로 잘 드러나지 않는 영역일 수도 있습니다. 내면적인 부분까지 완성도를 추구하는 사람이 백엔드 개발을 하기에 더욱 적합합니다.

서버 프로그램은 24시간 실행된다는 점이 백엔드 개발자들에게는 고충이기도 합니다. 긴급한 상황에 대비하기 위해서 노트북 컴퓨터를 항상 가지고 다니는 사람이 많습니다. 회식자리에서도 노트북을 켜고 장애 상황에 대응하는 경우도 있습니다. 그런 부담을 줄이기 위해서 규모가 큰 서비스에서는 당번(oncall)을 정해서 통상적인 휴식 시간대에는 돌아가면서 대응을 하기도 합니다.

향후 전망

다양한 도구와 프레임워크가 개발되고 있는 상황에서 백엔드 개발자의 미래는 어떻게 될지 백엔드 개발에 어떤 비전이 있다고 생각하시나요?

새로운 도구와 프레임워크로 인해 개발자의 수요나 가치가 하락하지 않을까 하는 우려도 있습니다. 하지만 현장에서 느껴지는 바로는 우리가 해야할 과제가 늘어가는 속도에 비하면 도구 등에 의한 생산성 증가 속도는 오히려 느리다고 느껴집니다. 새로운 시스템에 대한 수요와 유지 보수할 기존의 시스템도 늘어만 간다는 느낌입니다. 혁신에 투자할 수 있는 여력을 가진 사회라면 S/W개발자에 대한 투자는 계속 증가할 것이라고 전망합니다. 새로운 도구/프레임워크는 기존의 문제를 해결하고 개선하기 위해 나온 것입니다. 기존의 기술을 쓰면서 깊이 있는 고민을 한 사람이라면 새로운 도구에 더 빨리 적응하고 응용할 수 있을 것입니다. 따라서 새로운 도구와 프레임워크들도 기존의 개발자에게 더 큰 기회가 될 수도 있습니다.

다만 개발 분야에 따라서는 흥망성쇄가 엇갈릴 수도 있습니다. 예를 들면 이전에는 Flash기반의 UI개발에 대한 수요가 많았습니다. 하지만 HTML 내부에서 화려한 UI를 구현할 수 있도록 기술환경이 변화되면서 이전만큼 Flash 개발자를 찾지 않게 되었습니다. 모바일에서 Symbian OS를 바탕으로 개발을 했던 분들 익숙했던 개발플랫폼이 저물어가는 모습을 지켜봐야 했습니다.

지금까지는 개발자에 대한 수요가 계속 늘었기에, 개발분야를 바꿀 수 있는 기회도 많았습니다. 기존 기술을 깊이 있게 이해한 분들은 새로운 환경에서도 뛰어난 개발자로 정착하는 모습을 많이 봤습니다. Flash에서 사용하는 ActionScript를 잘 쓸 수 있었던 개발자들이 웹FE개발자로 전환하는 경우는 많습니다. Android 초창기에는 같은 Java 언어를 썼던 서버개발자보다 FE개발자 출신 분들이 훨씬 빠르게 적응한다는 이야기를 들었었습니다. UI에 대한 패턴, 프로그래밍 모델들은 기존에 FE 개발을 하신 분들에게 익숙한 것이었기 때문입니다.

기존에 있던 개발 분야들도 깊게 발전하면서 이전에 없던 전담자가 생기고 있습니다. 하지만 한번 맡은 분야를 끝까지 담당한다는 보장은 없습니다. 실무에서는 갑자기 새로운 업무가 필요해졌을 때 사람이 채용될 때까지 기다릴 수도 없습니다. 그래서 새로운 개발분야에 적응할 수 있는 역량은 키워두는 것이 좋습니다. 지금 하는 업무와 연관성이 있는 분야에 관심이 있다면 그런 준비가 자연스럽게 이루어질 수 있습니다.

백엔드 개발은 여러 분야와 연결되기에 그런 면에서 무난한 선택입니다. 포용하는 분야가 많고, 이론과 실무경험이 잘 조화될 수 있기 때문입니다. RDB, TCP/IP, OS, 통계 등의 바탕이 되는 지식들은 몇십 년 전부터 축적되면서 발전된 것들입니다. 그래서 실무에 들어가기 전에 학교에서부터 준비할 수 있는 지식이 많기도 합니다.

머신러닝이나 빅데이터 기술의 발달이 백엔드 개발에 어떤 영향을 미칠 것으로 예상되나요?

해당 분야들은 급격한 수요 증가로 인해 여러 방면으로 전문가를 수혈하고 있습니다. AI같은 분야는 학교에서 이론적으로 준비가 된 분들이 회사에 들어오는 경우가 많아 보입니다. 기존에 백엔드 개발을 하시던 분이 해당 서비스의 필요해 의해 해당 분야의 전문가로 성장한 경우도 있습니다. 예를 들어 어떤 서비스의 추천 모듈을 1~2명이 개발하다가 고도화를 위해 전담 팀을 따로 분리한 경우도 있습니다.

백엔드 개발을 하다가 데이터 분석, 모델링 업무에 참여하는 경우도 더 늘어날 것 같습니다. 이론적인 바탕이 튼튼하더라도 특정 분야에 의미있는 분석/모델링을 하기 위해서는 그 도메인을 이해하는 것이 중요합니다. 서비스를 개발하면서 사용자의 특성을 이해한 개발자가 이론적인 깊이까지 갖춘다면 가치가 더 높아질 수 있습니다.

직접적으로 분석/모델링을 수행하지 않더라도 많은 백엔드 개발자는 이를 뒷받침하는 개발을 하게 될 것입니다. 예를 들면 깊이 있는 데이터 분석을 위해서는 데이터를 단순히 RDB같은 하나의 저장소에 쌓아두는 것만으로는 부족합니다. 이전에는 그냥 버렸던 로그성 데이터들도 어떻게 활용할 수 있을지 분석하기 좋게 저장을 할지 고민을 더 하게 되었습니다. 데이터를 다른 저장소로 옮기는 일도 용량이 클 때는 쉬운 일이 아닙니다. 무엇보다 해당하는 서비스가 많은 사용자와 데이터를 받을 정도로 운영되어야 머신러닝 같은 고급기법으로 분석할 만큼 많은 데이터가 쌓일 것입니다. 넓게 보면 대용량의 서비스를 개발하는 백엔드 개발자는 AI/빅데이터와 시대의 과제와 맞닿아 있습니다.

다른 분야로의 확장

백엔드 개발이라는 포지션에서 다른 영역(경영, 기획 등)에 관여하는 게 현실적으로 가능한지

프로젝트에 따라서는 서비스/제품의 기획 단계에서 개발자의 의견이 많이 반영됩니다. 개발자가 사용하는 플랫폼을 만들 때에는 더욱 그러합니다. 개발업무를 개선하기 위해 만드는 자동화 도구들, 모니터링 도구들도 큰 방향성을 잡는 사람은 개발자일 수 밖에 없습니다. 비개발자들이 사용하는 서비스들도 최근 들어서도 개발자의 의견이 많이 반영되고 있다고 느낍니다. 특히 구체적인 데이터 구조를 고려하는 백엔드 개발자가 초기에 의견을 주면 효율적인 방향으로 기획이 이루어질 가능성이 높아집니다. 기획자, UX설계자 역할을 하시는 분들 중에서도 개발자 출신들이 있기도 합니다.

'경영'의 영역에 관여하는 것은 개발 조직에서 조직장이 하는 역할입니다. 어떤 일에 어느 정도의 투자를 하고, 사람을 얼마나 채용할지, 어떻게 보상을 할 건지가 모두 경영적인 판단이라고 할 수 있습니다. 개발자 중 일부는 그런 역할을 하는 사람이 될 수도 있습니다. 하지만 그런 일이 모든 사람의 적성에 맞는 일은 아닙니다. 조직장은 기술뿐만 아니라 사람 사이의 일에 관심을 깊이 가져야 잘 할 수 있는 일이라고 생각합니다. 뛰어난 개발자가 사람 사이의 문제를 잘 해결할 수 있으리라는 보장은 없습니다. 기술을 깊이 파고 널리 쓰이는 프로그램을 만들고 지식을 잘 전파하는 일에서 행복을 더 느끼는 개발자들이 더 많을 것 같습니다. 그런 일로도 긍정적인 영향력을 크게 미친다면 조직 내에서 중요한 존재로 대우받을 것입니다.

백엔드 개발에 필요한 지식

백엔드 개발을 하려면 어떤 지식이 중요하고 무엇을 기본적으로 알아야 할까요? 백엔드 개발 전문가는 어떤 언어, 툴, 주제를 공부하고 개발하나요? 프로젝트 스킬셋을 결정하는데 고려되는 부분들은 어떤 것들이 있나요?

백엔드 개발에서 중요하다고 생각되는 지식이 무엇인지에 대해서 많은 분들이 질문해 주셨습니다. 저는 주로 JVM과 Linux를 바탕으로 한 환경에서 서버 모듈 개발을 했습니다. 그래서 앞으로 이어지는 이야기에서 여러 기술 생태계를 다양하게 담지는 못했다는 점을 감안해 주셨으면 합니다. 웹 서버를 개발할 때는 아래와 같은 요소들이 우선 떠오릅니다.

  • 웹 생태계의 스펙
    • HTML, HTTP(1.1 , HTTP/2)
  • 기본 SDK, 라이브러리/프레임워크 이해와 활용
  • 클라이언트를 위한 API 설계
  • 서버/컴퍼넌트/객체 간의 역할 분담/의존성/통신 방법 설계
  • 저장소 활용
    • DBMS 설계
    • Cache 적용
      • Global/Local cache 적용범위, 라이프 싸이클, 솔루션 선택
    • 파일 저장 정책/솔루션 선택 활용
  • 검색엔진 연동 방식 결정
  • 빌드 도구
    • Maven/Gradle
  • 배포 전략
  • 성능 테스트/프로파일링/튜닝
    • JVM 레벨의 튜닝 (GC 옵션 등)
      • 웹 서버(Nginx,Tomcat)등의 설정/튜닝
    • OS 설정의 주요 값 확인
  • 인접 기술에 대한 이해
    • DBMS, Front End 등
  • 서버 개발자에만 해당하지는 않는 항목
    • 테스트 코드 작성/리팩토링 기법
    • 버전 관리 전략
      • branch 정책 등

개발자 한 명이 위에 언급한 모든 요소를 깊이 잘 알아야 프로젝트에 참여할 수 있어야한다는 의미는 아닙니다. 팀을 이룬 개발자들의 지식들이 합쳐져서 구현 방식과 정책이 결정됩니다.

프로젝트의 스킬셋을 정할 때에도 보통 구성원 중 적어도 한명은 경험이 있는 기술들을 택합니다. 사용자가 많은 서비스라면 프로그램이 돌아가는 만드는 것만이 목적이 아니기 때문에 기술의 선택에 더 조심스러워집니다. 운영환경에서의 모니터링과 문제해결까지 팀 구성원이 할 수 있는 기술을 선택합니다. 내부의 소수만 사용하는 관리 도구를 만들 때는 상대적으로 부담 없이 팀원 모두가 처음 쓰는 기술을 적용하기도 합니다. 새로운 기술은 위험성이 적은 서비스에 적용해서 경험을 키운 이후에 큰 서비스에 적용을 합니다.

데이터베이스

데이터베이스를 어디까지 알아야하나요?? 데이터베이스를 어떻게 활용하는 것이 효율적인가요? 쿼리를 어떻게 만들고 튜닝해야할까요?

사용자의 요청량과 저장 용량이 많은 서비스에서는 하나의 저장소만을 쓰지는 않습니다. 네이버의 서비스에서도 MySQL, CUBRID, Redis, Memchaced, HBase, MonoDB, Elasticsearch 등 다양한 저장소를 활용하고 있습니다. 네이버와 라인에서 Arcus, Elasticsearch, Cassandra, Redis, HBase와 같은 다양한 저장소가 쓰인 사례는 아래 글을 참고하실 수 있습니다.

다양한 저장소가 쓰이는 시대에도 RDB(관계형 데이터베이스)는 여전히 가장 우선시되는 저장소입니다. 그래서 RDB를 잘 다루는 능력은 백엔드 개발자의 핵심 역량 중 하나입니다. 개발을 하는 도중에도 쿼리의 호출 횟수나 실행 계획이 비효율적이지 않은지 확인하는 습관이 필요합니다. 운영 중에도 느린 쿼리를 모니터링하고 DBA와 협업하여 성능 개선을 하는 작업을 실무개발자들은 꾸준히 하고 있습니다. ORM같은 추상화된 프레임워크를 써서 직접 SQL을 작성하지 않는 경우에도 그런 작업들은 더욱 중요합니다.

대용량 서비스들을 보면 DB 쿼리를 만드는 스타일이 과거와는 달라졌습니다. 과거에는 서버 간의 네트워크 호출 비용을 줄이기 위해 굉장히 많은 테이블을 한번에 조인하는 긴 SQL을 만드는 경우가 많았습니다. 하지만 요즘은 복잡한 JOIN은 가급적 피하는 경향이 생겼습니다. 데이터를 조회하는 SQL이 단순할수록 데이터를 다른 저장소에 캐시하거나 분산해서 저장하기가 쉬워집니다. 대용량을 저장하는 UGC 서비스에서는 RDB 테이블 사이의 JOIN은 최대한 제약을 하고 어플리케이션 레벨에서 여러 저장소의 연관된 데이터를 조합하기도 합니다.

Stored prodecure도 가급적 사용하지 않는 경우가 많습니다. DB안에서 실행되는 Stored procedure는 급하게 개발된 서비스에서는 많이 사용되었습니다. 네트워크 호출비용이 없어서 성능에 이득이 있고, DB안에 저장되니 배포절차가 단순했기 때문입니다. 그러나 길게 작성된 Stored prodecure는 만들었던 사람도 수정하기 힘든 경우가 많습니다. 데이터와 독립적으로 로직을 테스트하기도 어렵습니다. 별다른 배포절차가 없으니 버전관리가 제대로 되지 않는 경우가 많았습니다. 그리고 데이터의 연산에 DB서버의 CPU 자원을 소모함으로서 서비스가 커가면서 DB에 병목이 될수 있는 가능성을 더 키울 수 있습니다. 이런 이유로 초기에 Stored procedure로 개발했던 로직을 어플리케이션 단으로 빼는 작업이 서비스가 성장하는 과정에서 흔하게 일어납니다.

DB서버 1대로 트래픽이나 저장량이 감당이 안 될 때, 이떻게 이를 분산할지도 항상 어려운 과제입니다. 성능 향상을 위해서 Local cache, Global cache를 동원하기도 합니다. 어느 정도 복제지연(Replication replay)이 그다지 민감하지 않은 서비스에서는 쓰기 작업은 Master 노드로, 읽기작업은 복제되는 Master의 데이터를 복제한 여러 대의 Slave로 DB를 구성하기도 합니다. 총 저장되는 용량이 많을 때에는 여러 개의 DB인스턴스에 이를 나누어서 저장하기도 합니다. 이를 샤딩이라고 부르는데 Sharding Platform에서 자세한 개념을 참조하실 수 있습니다. SpiderMySQL fabric와 같은 솔루션으로 샤딩을 할 수 있고 네이버 내부에서는 이런 용도로 자체 개발한 Nbase-T라는 저장플랫폼을 쓰고 있습니다. 샤딩의 기준이 되는 키가 명확하면 나누어서 저장하기가 상대적으로 쉽습니다. 개인화 서비스의 경우에는 사용자의 ID별로 샤드키를 잡으면 자연스럽습니다. 그런데 샤드키를 어떤 것으로 잡아야 할지 명확히 확신이 서지 않는 서비스가 있을 수 있고, 중간에 샤드키를 바꾸는 비용은 굉장히 큽니다. 어떤 솔루션을 쓰든 RDB는 사용량이 늘어났을 때 분산하는 비용이 비쌉니다. 그래서 성장할 가능성이 큰 서비스라면 RDB의 자원을 아껴서 쓸 필요가 있습니다.

개발툴

Jenkins, AWS 등 Backend에 도움이 되는 도구를 배웠지만 도구를 배운다는 게 과연 실력을 키우는 것인지는 의문입니다.

개발도구를 잘 활용하는 능력은 생산성과 직결되기에 중요합니다. 그런데 개발도구를 '배워야'하는 개발자보다는 스스로 익힐 수 있고, 적절한 도구를 선택할 수 있는 개발자가 현장에서는 필요합니다. 특정 개발도구를 익혔다는 사실은 단기적으로는 실력이라고 할 수 있습니다. 새로운 도구가 나왔을 때도 적응할 수 있는 학습력/적응력/판단력이 본질이고 이것이 누적되어 실력이 됩니다.

개인적으로는 아래와 같이 개발자의 수준을 분류하고 싶습니다.

  • 레벨0: 이미 쓰고 있는 개발도구의 사용법을 알려주거나 가이드 문서를 줘도 잘 못 씀
  • 레벨1: 알려주거나 같은 팀에서 만든 가이드 문서에 있는 만큼만 쓸 수 있음
  • 레벨2
    • 개발도구의 공식 레퍼런스를 보고 사용법을 스스로 익힐 수 있음
    • 자신이 경험한 사용법을 문서화해서 팀 내에 전파할 수 있음
  • 레벨3
    • 여러 개발도구를 비교 분석해서 상황에 적합한 도구를 선택할 수 있음
    • 공식 레퍼런스 문서에서 부족한 부분을 수정해서 기여할 수 있음
  • 레벨4
    • 개발도구의 문제를 소스 코드를 수정해서 Fork/패치해서 사용할 수 있음

신입사원이라도 레벨2 정도는 함께 일할 개발자에게 기대를 하게 됩니다.

병렬처리

병렬처리를 어떻게 도입하면 좋은가요?

Servlet기반의 Java웹서버들은 기본적으로 사용자의 요청을 병렬적으로 처리합니다. 그래서 객체가 멀티스레드에서 공유되는 것인지, 아닌지를 의식하는 일은 중요합니다. 클래스의 멤버변수에는 항상 멀티스레드에서 접근해도 안전한(Thread-safe)한 변수만 두면 된다는 단순한 규칙만으로도 많은 문제를 예방할 수 있습니다. 그런데 Local cache를 적용할 때는 멀티스레드에서 공유된 객체가 쉽게 눈에 안 띌 수도 있습니다. 그래서 Cache대상의 객체는 Immutable하게 유지하는 것이 안전합니다. 직접 Cache 모듈을 만드는 경우 위험한 버그가 생기는 경우가 많습니다. 하나의 메모리 누수를 잡기까지 도 그런 사례입니다.

사용자 요청을 처리하는 스레드 외에도 별도의 스레드 풀로 실행해야 할 작업이 종종 생기기도 합니다. 예를 들면 사용자에게 주는 응답에 영향을 주지는 않지만 실행시간이 길어질 수도 있는 update 구문을 DB에 실행하는 작업같은 것입니다. 그런 경우 Java에서는 ExecutorsThreadPoolExecutor에 있는 많은 옵션들이 정교한 제어를 하는데 도움이 됩니다. new Thread()로 직접 스레드를 생성하는 방식은 JDK5 이후로는 권장하지 않습니다.

보안

다양한 보안 위협 상황에 어떻게 대처해야 하나요?

널리 알려면 XSS, CSRF, SQL Injection 공격에 대해서 대처하는 방법은 모든 개발자가 알고 있어야 합니다. 실무에서 일하다보면 의존하는 라이브러리에서 발견된 보안 취약점에 대처한 경우가 많았습니다. 보통 버전업을 하거나 회사 내에서 자체적인 패치를 하기도 했습니다. 보안 취약점이 자주 발견되는 Struts2 같은 프레임웍들은 신경써서 대처하기 위해 주요 변경을 알리는 메일링 그룹에 가입하기도 했습니다.

테스트

테스트가 얼마나 중요하다고 생각하는지 궁금합니다. 실제 서비스에서는 테스트 코드를 어떻게, 어느 정도로 짜는가요?

많은 사람이 협업해서 개발하고 지속적으로 개선해나갈 소프트웨어라면 테스트 코드를 작성하는 일은 더욱 중요합니다. 테스트 코드를 작성하는 능력도 백엔드 개발자의 핵심역량 중 하나이기도 합니다. FE개발이 분업화되고 서비스간의 API 호출이 많아지면서 최근 백엔드 개발자의 업무는 API 서버 개발에 더 집중되고 있습니다. 따라서 최종적으로 UI와 통합하기 전에 개발한 API를 스스로 테스트해야 할 필요성이 더 커졌습니다. 오류를 있다는 제보를 다른 개발자로부터 받아서 수정 후 재배포하고 다시 알리는 비용은 스스로 오류를 발견했을 때보다 굉장히 크기 때문입니다. HTTP API에 대한 테스트는 작성하기도 쉽고 작성했을 때의 이득도 큽니다. 최근 진행 중인 프로젝트에서는 Rest-assured 와 Spring MVC Test Integration 을 이용해서 HTTP API를 통합 테스트 하고 있습니다. 라이브러리나 개발플랫폼을 개발하는 경우에도 테스트 코드는 중요합니다. 수천 대의 서버에 배포되는 라이브러리에서 사소한 수정을 해서 배포한 적이 있었는데, 최대한 모든 경로를 상상해서 테스트 코드를 작성할 수 밖에 없었습니다. 라이브러리 배포 후 이를 적용한 서비스에서 오류가 발생했을 경우 재배포하는 과정이 비용도 크고 신뢰를 잃게하는 요인이 되기 때문입니다.

'시간이 없어서 테스트를 못 만들었다’는 말은 '나는 테스트 코드를 만드는데 시간이 많이 걸린다’는 말과 동일합니다. 해당 언어에 대한 숙련도가 떨어지는 사람일수록 테스트 코드를 작성하는데 부담을 크게 느끼는 경우도 많이 봤습니다. 능숙해질수록 테스트 작성 시간은 줄어들어 테스트에 투자한 대비 이득이 커집니다. 가끔 테스트를 작성할 때 이 테스트 덕분에 어느 시점에 실제적인 이득이 있을지 나누어 생각할 때가 있습니다. 예를 들면 아래와 같습니다.

  • 유지보수 기간의 생산성을 높여주고 새로 프로젝트에 투입될 사람에게도 이득을 주는 테스트
  • 프로젝트 오픈 일정 직전까지의 코드 변경과 버그 발견에 도움을 주는 테스트
  • 오늘 당장 프로그램을 목표한 곳까지 작성하는 일을 더 빨리 마치게 해주는 테스트

테스트 코드를 작성하다 보면 실제로 당장 할 일을 더 마치게 빠르게 해주는 테스트도 만나게 됩니다. 복잡하게 얽힌 프로그램을 개발할 때는 최종적인 UI를 통해서 수동으로 테스트하기 전에 부분적으로 잘라서 테스트하는 것이 버그를 쉽게, 빨리 잡는데 도움이 됩니다. 예를 들어 일주일 걸려서 만든 프로그램을 마지막 날에 한번에 테스트한다면 디버깅에 훨씬 시간이 많이 걸릴 것입니다. 몇 달 간 진행하는 프로젝트에서 단 한 줄의 테스트 코드도 안 짜는 분이 있다면 당신의 지금 능력으로도 그 방식이 오늘 일을 가장 빨리 마치는 방법이 아닐 것 같다고 말씀드리고 싶습니다. 그리고 테스트를 적극적으로 짜다보면 오늘 하루를 넘어서서 더 큰 이득을 주는 테스트를 만들어서 다른 사람의 생산성에도 긍정적인 영향을 미칠 수 있을 것입니다.

실제로 테스트로 인한 긍정적인 경험을 쌓아가다 보면 더 넓은 범위와 다양한 기법으로 테스트 코드를 작성하는데 동기유발이 됩니다. 제 개인적인 경험을 돌아보면, 점진적으로 테스트 코드를 작성하는 범위를 늘려가는 방식이 도움이 되었습니다. 처음에는 테스트가 쉬운 Utilty 클래스에 대한 테스트부터 작성했습니다. 전에도 간단한 유틸리티에 대한 테스트는 main메서드 안에서 하기도 했는데, 그런 코드를 JUnit 안으로 옮기니 반복해서 실행하고 결과를 확인하기에 훨씬 편해졌습니다. 그 이후에는 Spring framework의 통합테스트 기능을 이용한 테스트를 작성하기 시작했습니다. 특히 테스트 코드에서 DB에 입력한 데이터를 자동으로 롤백시키는 기능이 DB와 연동된 테스트를 할 때 유용했었습니다. 이후에 더 정교한 테스트를 하려다 보니 테스트용 객체를 만드는 프레임워크인 Mockito를 사용하게 되었습니다. 테스트 코드를 먼저 작성하는 기법도 사용할 수 있게 되었습니다. 저는 실무에서 테스트코드를 작성하기 시작한 후부터 6개월정도가 지나서야 Mockito와 같은 라이브러리의 필요성을 느끼게 되었었습니다. 처음으로 시도한 프레임워크는 EasyMock이었는데, 그 당시에는 프레임워크 제목처럼 전혀 Easy하게 느껴지지 않았습니다. 근래에는 프레임워크의 발달로 이전보다는 테스트 코드를 작성하기도 쉬운 구조로 모듈을 구성하기도 쉽고 테스트 코드 작성 자체도 훨씬 편해졌습니다. 따라서 이제는 대부분 제가 겪었던 단계적인 과정을 훨씬 더 빠르게 경험하실 수 있으실 것이라 예상합니다.

자료구조/알고리즘

학교에서 배운 알고리즘, 자료구조를 어떻게 실무에서 적용하나요? 실무에서는 기본적인 알고리즘을 직접 구현하지는 않습니다. 이미 기본적인 SDK나 많이 사용되는 라이브러리 솔루션에서 자주 쓰이는 자료구조, 알고리즘은 구현이 되어 있기 때문입니다. 하지만 그러한 이미 구현된 솔루션을 잘 선택하고 활용하기 위해서는 그 바탕이 되는 지식이 중요합니다.

JDK의 Collection framework의 소스를 볼 때에도 기본적인 자료구조에 대한 이해가 필요합니다. Java HashMap은 어떻게 동작하는가?와 같은 깊이 있는 분석도 그런 배경지식을 바탕으로 하고 있습니다. 대용량 데이터를 어떻게 저장하고 탐색할지를 결정할 때도 자료구조는 중요합니다. LINE 소셜 네트워크 서비스의 아키텍처SSD는 소프트웨어 아키텍처를 어떻게 바꾸고 있는가? 의 기사에서 B-Tree, B+Tree를 어떻게 활용했는지도 참고할 수 있습니다. 이미지 처리처럼, 특화된 분야의 알고리즘이 실무에서 응용되는 경우도 있습니다. PHOLAR의 흔들림 보정 원리가 그 예입니다.

개발 프레임워크

백엔드 개발 프레임워크의 트렌드는 어떤가요?

제가 주로 관심을 가지는 JVM 생태계에서는 계속 Spring/Netty 기반의 프레임워크들이 꾸준한 발전을 하고 있다고 느껴집니다. 제 주변에 있는 여러 개발팀에서는 고부하 처리를 위한 서버에는 Netty를 바탕으로 UI개발과 직접 호출되는 API 서버나 HTML까지 그리는 서버 개발에는 Spring 계열이 많이 쓰이고 있습니다.

비동기 I/O를 활용하는 서버 개발이 최근 몇 년 동안 서버개발자들 사이에서는 많은 관심을 끌고 있습니다. Java 생태계에서는 보통의 웹어플리케이션을 개발하는데까지는 비동기방식의 개발이 확산되지는 못했습니다. 여러가지 이유가 있겠는데, 비동기 개발 자체가 가지는 진입장벽과 기술적인 난이도가 있기도 합니다. RDB를 호출하는 어플리케이션이 대부분인데 이를 뒷받침해야 할 비동기 JDBC 스펙의 구현체가 공식적으로 나오지 않은 탓도 있습니다. 최근 나온 Spring 5에서 비동기 IO개발을 지원하는 Webflux 모듈도 주목을 받고 있습니다.

모든 서버 어플리케이션이 비동기로 개발되어야 할 필요는 없다고 생각합니다. 그러나 트래픽이 많아서 동기/쓰레드 기반으로는 자원을 효율적으로 쓸 수 없는 시스템에서는 택할만한 선택지입니다. 네이버에서 고부하를 처리하는 플랫폼의 서버는 Netty/Vert.x 위에서 비동기 방식으로 개발된 것이 많습니다. 대용량 세션을 위한 로드밸런서 도 그 예입니다.

Serverless

Serverless 아키텍처에 대해 궁금합니다.

Serverless라는 용어가 자극적이기에 널리 퍼졌다는 생각도 듭니다. 주로 BaaS와 FaaS에 의존하는 아키텍처를 Severless라고 주로 부릅니다. AWS의 람다나 네이버 클래우드 플랫폼의 Cloud Functions에 Servleless 아키텍처를 지원하는 플랫폼입니다. 요즘은 그 의미를 더 넓게도 쓰고 있습니다. 폭넓게는 어플리케이션 코드를 작성하는 개발자가 서버관리를 신경쓰지 않는 구조나 역할분담을 해당 어플리케이션 입장에서는 'Serverless'라고 부르기도 합니다. 외부로 공유하는 글에 Serverless 라는 용어를 쓰게 된다면 마틴파울러의 블로그에 올라온 https://martinfowler.com/bliki/Serverless.html 와 같은 글을 읽어볼 만합니다.

Serverless와 같이 인프라 환경이 고도로 자동화/추상화된 환경에서는 이제 전통적인 어플리케이션에서 했던 JVM, 커널 파라미터, 웹 서버 튜닝이 이제 필수 지식이 아니라고 생각할 수도 있습니다. 그러나 인프라의 기반 구조를 잘 이해하면 추상화된 서비스도 잘 쓸 수 있게 될 여지도 있습니다. 네이버와 같은 회사에서는 내부적으로 BaaS, FaaS를 만들기도 하기 때문에 그런 프로젝트에 참여하는 개발자들에게는 최종 사용자 격인 개발자들보다 숨겨진 레이어에 대한 지식이 더 필요하기도 합니다.

실무에서 하는 고민

이 단락은 구체적인 질문은 없었더라도 실무에서 반복적으로 하는 고민들을 떠오르는 대로 정리해봤습니다.

용어의 범위

협업하는 사람들 사이에서 용어의 정의가 명확하지 않다면 갈등이 생기기 쉽습니다. 익숙하게 쓰고 있는 용어일지라도 그 정의에 대해서 한번도 찾아본 적이 없다면 원래의 의미를 잘못 이해했을 가능성이 높습니다. 책이나 인터넷 동영상에서도 정밀하지 못한 용어를 쓰는 경우가 꽤 많습니다. 때로는 용어의 정의 자체가 논쟁적인 경우도 있습니다. 그런 경우 어떤 논란이 있는지 이해를 한다면 그 용어를 피하거나 합의점을 찾아가는데 도움이 됩니다.

HTTP 프로토콜 위에서 JSON혹은 XML의 형식으로 통신하는 API를 폭넓게 REST API라고 부르는 경우가 많습니다. 그런데 현업에서 많은 이들이 REST라고 부르는 API들은 창시자인 Roy Fielding의 기준으로는 REST가 아닙니다. 대표적으로 상태가 Hyper link를 통해 전이되어야 한다는 HATEOAS를 대부분의 API를 충족시키지 않습니다. 이에 대해서는 그런 REST API로 괜찮은가를 참조하실 수 있습니다. REST API의 범위에 대한 논란을 피하고 싶다면 HTTP API 혹은 Web API라고 칭하는 것이 무난합니다.

테스트 코드를 작성하는 일을 통칭해서 TDD (Test Driven Develop)라고 부르는 사람도 있습니다. 이미 운영서버에서 잘 돌아가는 코드에 테스트 코드를 추가하면서 커밋로그에 'TDD 코드 추가'라고 적어놓은 경우도 본 적이 있습니다. 그러나 TDD의 개념을 정리한 Kent Beck이 'Test Driven Development: By Example' 책에서 보여준 기법은 테스트 작성, 테스트를 통과하는 코드 작성, 리팩토링의 싸이클을 거치는 것입니다. 테스트가 없던 레거시 코드의 버그를 고치거나 리팩토링 하기 전에 테스트를 추가하는 것도 TDD의 싸이클의 일부일 수도 있습니다. 그렇지 않은 경우에 뒤늦게 테스트를 추가하는 작업은 TDD라고 부르기보다는 '테스트 코드를 작성한다.'라는 표현이 무난합니다.

비슷하게 JUnit으로 작성하는 코드를 모두 '단위 테스트'라고 부르는 사람도 있습니다. JUnit의 이름으로 인해 생긴 오해입니다. JUnit으로는 다양한 범위의 테스트 코드를 작성할 수 있습니다. 예를 들면 스프링 프레임워크의 레퍼런스 메뉴얼에서도 단위 테스팅(Unit-testing)과 통합 테스팅(Integration-testing)이 구분되어 있습니다. 해당 메뉴얼의 단위 테스팅에 대한 설명에서는 아래와 같이 설명을 덧붙이고 있습니다.

진정한 단위 테스트는 실행시점에 구성되어야할 기반요소가 없기 때문에 대체로 굉장히 빠르게 실행된다. (True unit tests typically run extremely quickly, as there is no runtime infrastructure to set up.) https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#unit-testing

Spring 레퍼런스 메뉴얼의 통합 테스팅 절에서 설명하는 기법들도 JUnit을 쓰고 있습니다. 이를 봐서도 JUnit으로 만든 테스트 코드를 모두 '단위 테스트’라고 칭하는 것은 정교하지 못한 분류이고, 혼동의 소지가 있습니다.

VO (Value Object)라는 패턴 이름도 실무에서 혼란스럽게 쓰이고 있습니다. getter/setter만 있는, 값을 실어나르는 VO라고 칭하는 사람이 있는데 이는 DTO로 칭하는 것이 혼란의 여지가 적습니다. Core J2EE Patterns 라는 책의 초판에서 그 의미로 VO가 정의되어 있었지만, 혼동의 여지가 있어서 2판부터는 TO(Transfer Object)로 바뀌었었습니다. 아래 자료들에서 VO와 TO의 정의를 확인할 수 있습니다.

위의 자료들에 따르면 Value Object는 값에 의해 동등성이 판단되는 객체입니다. Getter/Setter만 가진 객체를 VO라고 생각한다면 ORM이나 DDD(Domain Driven Development)의 맥락에서 나오는 VO 용어를 접할 때 혼란이 올 것입니다. Java의 스펙 제안 문서인 JEP 169: Value Objects에도 VO는 getter/setter만 가진 객체를 의미하지 않습니다.

이렇듯 새로운 용어를 접할 때 엄밀한 정의를 검색해 보는 습관을 들인다면 위의 사례들과 같은 혼란에 더 잘 대처할 수 있습니다. 그 용어를 만든 사람이 한국인이 아니라면, 영어 자료를 구글로 검색해보는 것을 추천합니다.

더불어 내가 속한 회사에만 쓰이는 용어와 업계에서 범용적으로 쓰이는 용어도 구분할 줄 알아야합니다. 특히 큰 회사에 다니시는 분들이 회사 내에서만 통용되는 어를 회사밖에서도 쓰는 경우가 종종 보입니다. 예를 들면 네이버에서는 품질향상을 위한 활동을 QP(Quality Practice)라는 용어로 칭했었습니다. 어떤 분들은 이 용어를 QA처럼 외부에서도 쓰이는 것으로 생각하고 사용하시는 경우를 봤었습니다.

클라이언트와 백엔드의 역할분담

이전에는 JSP와 같이 HTML를 서버사이드에서 생성하는 기술이 주로 쓰였습니다. 화면 전체를 다시 그릴 필요가 없는 요청은 Ajax로 처리했지만, 서버 렌더링에 보조적으로 쓰이는 경향이 강했습니다. 하지만 요즘은 React.js나 Vue.js와 같은 프레임워크가 널리 쓰이면서 클라이언트 사이드에서 HTML을 생성하는 비중이 높아지고 있습니다. https://d2.naver.com 도 React를 이용해서 전적으로 클라이언트 사이드인 웹브라우저에서 HTML을 그리고 있습니다. 선택지가 더 생김으로 인해서 어플리케이션이나 페이지의 특성에 따라서 어느 방식이 적합할지 이전보다 더 많이 고민하게 되었습니다. 사용자가 페이지에 처음에 진입할 때는 서버에서, 그 이후로는 클라이언트에서 HTML을 그리는 Universal Rendering도 최근 많이 시도되고 있습니다.

HTML 렌더링을 어디에서 할지는 개발자 간의 역할분담에도 영향을 미칩니다. 100% 클라이언트 사이드 렌더링을 하는 구조는 서버 개발자가 HTTP API 개발에만 전념할 수 있다는 장점이 있습니다. 반면 전문 Front End 개발자의 작업이 더 많아지는 부담도 생깁니다. 제가 속한 조직에서는 백엔드 개발자들도 React.js나 Vue.js를 이용한 UI개발자 참여함으로써 그런 단점을 보완하고 있습니다.

클라이언트를 위한 API 설계

앞서 언급한 REST 스타일의 일부를 차용하더라도 HTTP API의 설계의 많은 부분은 매번 프로젝트마다 고민해야할 점이 많습니다. 예를 들면 단순 CRUD(입력/조회/수정/삭제) API는 각각 POST/GET/PUT/DELELE의 HTTP 메서드로 연결시키더라도 이를 벗어난 기능들은 어떻게 설계해야할지 명확하지 않은 경우가 많습니다. 페이징 처리를 할 때나 복잡한 검색조건이 있을 때의 파라미터 표현방식에서도 서비스마다 다르게 정의하고 있습니다. 그리고 API에서 선택적으로 반환할 속성과 아닐 속성을 지정하는 방식도 다양합니다. 아래의 스펙들이 보다 구체적인 API 설계안들을 제안하기는 합니다. 그러나 아직 어떤 상세한 스펙이 지배적으로 많이 쓰이고 있지는 않은 상황입니다.

클라이언트 사이드에서 UI 렌더링을 하는 경우가 늘어나면서도 HTTP API를 더 다양하고 정교하게 사용하고자 하는 필요성이 늘었습니다. 이러한 흐름 속에서 Facebook에서는 GraphQL을 내 놓았고, Github에서도 이를 도입하는 등 점점 확산되고 있는 추세입니다. 현시점에서 기존의 소위 REST 스타일과 유사한 틀에서 나름대로의 관례를 정립해서 최선을 추구할지, GraphQL을 도입할지도 고민되는 선택 중의 하나입니다.

시스템을 어떻게 자를것인가

시스템을 만들 때 많은 고민들은 결국 '구성요소 간의 역할과 책임을 어떻게 나눌 것인가'로 표현할 수 있습니다. 적게는 메서드, 클래스, 패키지 사이의 분담을, 크게는 jar로 배포되는 모듈이나 API로 통신을 하는 컴퍼넌트를 나누고 통신을 하는 방법을 고민을 하게 됩니다. 그리고 소프트웨어가 나누어진 경계에 맞추어 사람이 담당하는 업무의 범위나 협업 방식이 정해집니다. 그런 구조가 잘 잘라져 있어야 많은 사람들이 동시에 협업하면서 소프트웨어를 개발할 수 있습니다.

최근 MSA(Micro Service Architecture)라는 구조가 각광을 받으면서 서버에 배포 가능한 모듈의 단위를 이전보다 작게 가져가는 경향이 나타나고 있습니다. 많은 개발자가 동시에 협업하면서 개발하는데 MSA가 장점이 있기 떄문입니다. 이전에는 MSA와 같은 구조로 서비스를 만드는 것이 비용이 더 컸었습니다. 서버/네트워크를 더 많이 사용해야 하고, 서버/구성요소마다 설정하는 시간이 더 들어가고 문제가 생겼을 때 모니터링과 추적을 하는 것도 중간에 원격호출이 없을 때보다는 쉽지 않았습니다. 그러나 요즘에는 인프라시스템, 모니터링, 프레임워크의 발전으로 과거보다는 작게 단위로 서비스를 쪼개는 비용이 내려갔습니다. 그렇지만 서비스 간의 경계를 어떻게 잘라야 할지는 여전히 어려운 일이고, 적절히 잘라지지 않은 경계를 나중에 바꾸는 일도 비용이 큽니다.

네이버의 서비스들은 상호 연관되어 있고 다양한 조직의 많은 개발자들이 협업으로 만들고 있습니다. 네이버의 첫화면에 들어가는 구성요소만 해도 다양한 조직이 담당하는 모듈들을 거치고 있습니다. MSA라는 용어가 나오기 전이었지만, 그리고 최근의 경향보다는 서비스의 단위가 큰 경우가 많았겠지만 MSA가 이루고자 했던 장점들은 네이버에서 오랫동안 실용적으로 추구되어 왔다고 느껴집니다.

네이버의 백엔드 개발

개발,배포 방식

실무에서 어떻게 협업을 하고 개발, 배포를 하나요? 개발팀이나 프로젝트의 성격에 따라서 일하는 방식은 다양합니다. 제가 참여하는 프로젝트 중 하나의 예를 들면 아래와 같습니다.

  • 주 1회 배포
    • 월요일 쯤에 회의로 이번 주 배포 범위를 확정. 목/금요일에 주로 배포
    • 치명적인 버그에 대한 Hot fix는 주1회 주기에 상관없이 배포
    • 최대한 무정지 배포
  • 버전 관리/ 코드 리뷰
    • master에서 추가할 기능별로 feature branch(topic branch)를 따고 주로 master를 목표로 PR(pull request) 요청
    • Github의 마일스톤으로 버전을 입력하고 PR마다 매핑
    • 한번에 반영하기 힘든 기능이나 큰 단계의 버전업은 별도의 feature branch로 merge하기도 함
    • 리뷰를 충분히 받았는지는 PR을 올린 사람이 판단하여 merge
    • 배포일이 다가오는데 기능 오류가 없는 것으로 검증된 branch는 일단 merge후 사후 리뷰를 하기도 함
  • DB 스키마 관리
    • 개발환경에서는 Liquibase 로 관리
    • 운영환경에는 Liquibase에서 생성된 SQL을 사내 스키마 관리 시스템을 통해서 요청
  • 테스트
    • PR를 올리면 CI서버에서 빌드 실행. API 서버의 테스트 코드와 FE코드에 검사도구인 Eslint의 검증을 통과해야 merge 가능
    • 현재는 서버 모듈만 테스트 코드 작성
      • 통합 테스트: JUnit + Rest assured + (Spring MVC Test framework 또는 Embeded Tomat 활용) 이용
      • 단위 테스트: JUnit + Mockito 활용
      • 특별히 의식하지는 않았는데 Line coverage 는 74% 정도
    • Docker를 이용한 테스트 환경 활용
      • 마크업 등 UI변경을 확인해야 하는 경우는 PR에 'Docker build'라는 라벨을 붙임
      • 그러면 docker 이미지가 만들어져서 사내 docker 이미지 배포용 cluster에 해당 branch를 확인할 수 있도록 배포가 됨
      • docker로 배포된 서버에서 어느 정도 테스트 후 merge
    • 주1회 배포전 QA시간을 따로 잡고 개발팀 전체가 함께 테스트. (전담 QA가 없는 프로젝트인 경우)
    • 테스트 시나리오는 그 주 변경된 기능과 영향받는 기능 위주로 작성
    • 큰 기능 추가, 전체적인 코드 변경 시점에는
      • 스테이징 서버에서 1~2주 정도 개발팀이 미리 운영 데이터로 시스템을 써보기도 함
        • 전체 기능에 대한 테스트 시나리오를 다 수행해보기도 함

인프라 기술 활용

네이버에서 클라우드 컴퓨팅 기술(IaaS, PaaS, SaaS)을 어떻게 활용하고 있는지 알고 싶습니다. 네이버 서버 개발팀과 IT Operations팀 간의 소통 방식과 코드 배포 방법, 또 모니터링 툴들에 대해서 알고 싶습니다

네이버 내부에서 가상 서버(VM)를 관리하는 방식은 IaaS에 가깝습니다. 개발망에서는 특별한 결재없이 개발자 누구나 직접 가상서버(VM)를 생성할 수 있습니다. 운영망에서는 예산 승인 절차 등을 거쳐서 SE에게 요청해서 VM 서버를 생성합니다. 물리서버도 필요하다면 사용 신청을 할 수 있는데, 가상서버에 비해서는 신청 절차에 시간이 더 걸립니다. 최근에는 Docker container를 이용해 더 운영효율성을 높이려는 시도도 진행되고 있습니다. 이에 대해서는 아래 자료들을 참조하실 수 있습니다.

사내에 많이 쓰이는 플랫폼은 조직마다 직접 설치하고 운영할 필요가 없도록 PaaS 형태로 제공되고 있습니다. 로그수집시스템 Nelo, 어플리케이션 성능 측정 도구인 Pinpoint, 성능테스트 도구인 nGrinder, 분산메모리 저장소인 NbaseARC이 그 대표적인 예입니다. 많이 쓰이는 오픈소스 솔루션도 PaaS화하여 운영하는 사례가 늘어가고 있습니다.

최종 사용자가 바로 서비스를 사용하는, 네이버 메일, 캘린더, 클라우드(NDrive) 등이 SaaS의 예라고 생각합니다. 네이버 내부의 IaaS/PaaS/SaaS 기술들은 Naver Cloud Platform ( https://www.ncloud.com/ )을 통해서 외부에도 제공되고 있습니다.

코드 배포는 내부적으로 만든 배포 솔루션을 사용하고 있고, 개발자 누구나 서버에 몇번의 클릭으로 서버에 배포할 수 있습니다. 개발 환경에서는 코드 Push나 Pull request가 올라오는 이벤트 등과 연결시켜서 자동으로 배포하기도 합니다. 운영환경에 배포하는 작업은 개발팀의 내/외부와 긴밀한 협의를 거쳐서 시점이 정해집니다. 배포 일정에 맞춰서 코드 리뷰, 소스 합치기, 테스트, 의존하는 다른 서비스에 공지하는 작업 등이 사전에 이뤄져야하기 때문입니다.

모니터링 도구는 여러가지를 복합적으로 사용하고 있습니다. 장비별 주요 자원 사용량, 네트워크, 어플리케이션 성능, 로그 수집 분석 등 모니터링 툴마다 특화된 영역이 다르기 때문입니다. 장애에 대한 모니터링을 하는 시스템은 빌드배포 시스템, 각종 모니터링 시스템과도 연동되어 있습니다.

네이버 내부에 정확히 'IT operation팀'이라는 용어는 쓰이고 있지는 않습니다. Nginx, Tomcat, JVM과 같은 솔루션의 설치와 설정 변경은 개발팀에서 직접하고 있습니다. 운영서버 VM의 생성, OS패치 등의 작업, 인프라 구성 등에 대한 컨설팅을 서비스마다 지정된 SE분들이 해주십니다. 개발DB의 설치와 스키마 변경은 개발팀에서 자율적으로 하고, 운영 DB에 대한 중요한 작업은 전담 DBA에게 요청합니다. 오랫동안 서비스를 개발,운영하면서 실용적인 프로세스가 정착되었다고 느껴집니다.

마치며

개발과 요리가 비슷한 면이 있다는 생각이 들 때가 있습니다. 몇 명이 먹을 요리를 정해진 레시피를 보고 혼자 만드는 일은 어렵지 않습니다. 그런데 많은 사람을 위한 음식을 여러 사람이 함께 준비하는 일은 차원이 다른 일입니다. 예전에 친척 분의 집에 방문했다가 샌드위치 500인분을 만드는 과정에 한두시간정도 참여한 적이 있습니다. 재료가 얼마나 필요할지 예측해서 구하고, 일하는 방식과 담당자를 정하고, 작업 시간을 계획하는 것 등 하나하나가 수월해 보이지 않았고, 그 중 하나만 어긋나도 샌드위치는 예상대로 만들어지지 않을 것 같았습니다. 그 일을 겪은 이후로 저는 큰 식당이나 결혼식 피로연에 갈 때마다 그 음식들을 준비하신 분들이 정말 대단하다고 느낍니다.

일단 실행은 되는 백엔드 프로그램을 만드는 일은 쉽습니다. 요즘은 특히 인터넷에 많은 재료와 레시피가 있기에 더욱 그러합니다. 그러나 협업하기에 좋은 방식으로, 성능과 안정성까지 고려한 백엔드 프로그램을 만드는 개발은 쉽지 않습니다. 그리고 모니터링과 데이터 수집,분석 등 사용자의 눈에 보이지 않는 영역들도 실무에서는 많은 비중을 차지합니다. 데이터나 사용자가 적었을 때에는 효율적이었던 구현 방식이 시스템이 성장하면서 문제의 근원지가 되기도 합니다.

블로그 이미지

낭만가을

,