본문 바로가기

독서, 유튜브/커리어

네이버 D2 주니어 백엔드를 위한 글 발췌

네이버 D2 아티클 중에 주니어 백엔드 개발자가 보면 좋을 글을 발견하였다.

https://d2.naver.com/news/3435170

 

특히 기억하고 싶었던 내용을 따로 발췌해 정리하였다.

 

보람과 고충

백엔드 개발자는 시스템을 안정적이고 효율적으로 만들 때 보람을 느낍니다. 사용자가 갑자기 몰려와도 에러 없이 서버 프로그램이 실행될 때, 성능을 획기적으로 개선했을 때, 모듈의 구조를 개선해서 코드를 많이 줄였을 때가 그런 경우입니다. 

 

 

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

 

백엔드 개발에 필요한 지식

  • 웹 생태계의 스펙
    • 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 등 다양한 저장소를 활용

 

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

 

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

 

Stored prodecure도 가급적 사용하지 않는 경우가 많습니다

 

DB서버 1대로 트래픽이나 저장량이 감당이 안 될 때, 이떻게 이를 분산할지도 항상 어려운 과제입니다. 성능 향상을 위해서 Local cache, Global cache를 동원하기도 합니다. 어느 정도 복제지연(Replication replay)이 그다지 민감하지 않은 서비스에서는 쓰기 작업은 Master 노드로, 읽기작업은 복제되는 Master의 데이터를 복제한 여러 대의 Slave로 DB를 구성하기도 합니다. 총 저장되는 용량이 많을 때에는 여러 개의 DB인스턴스에 이를 나누어서 저장하기도 합니다. 

 

어떤 솔루션을 쓰든 RDB는 사용량이 늘어났을 때 분산하는 비용이 비쌉니다. 그래서 성장할 가능성이 큰 서비스라면 RDB의 자원을 아껴서 쓸 필요가 있습니다.

 

병렬처리

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

 

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

 

테스트

일주일 걸려서 만든 프로그램을 마지막 날에 한번에 테스트한다면 디버깅에 훨씬 시간이 많이 걸릴 것입니다. 몇 달 간 진행하는 프로젝트에서 단 한 줄의 테스트 코드도 안 짜는 분이 있다면 당신의 지금 능력으로도 그 방식이 오늘 일을 가장 빨리 마치는 방법이 아닐 것 같다고 말씀드리고 싶습니다. 그리고 테스트를 적극적으로 짜다보면 오늘 하루를 넘어서서 더 큰 이득을 주는 테스트를 만들어서 다른 사람의 생산성에도 긍정적인 영향을 미칠 수 있을 것입니다.

 

테스트용 객체를 만드는 프레임워크인 Mockito를 사용

용어의 범위

HTTP 프로토콜 위에서 JSON혹은 XML의 형식으로 통신하는 API를 폭넓게 REST API라고 부르는 경우가 많습니다. 그런데 현업에서 많은 이들이 REST라고 부르는 API들은 창시자인 Roy Fielding의 기준으로는 REST가 아닙니다. 대표적으로 상태가 Hyper link를 통해 전이되어야 한다는 HATEOAS를 대부분의 API를 충족시키지 않습니다. 

 REST API의 범위에 대한 논란을 피하고 싶다면 HTTP API 혹은 Web API라고 칭하는 것이 무난합니다.

 

클라이언트를 위한 API 설계

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

 

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

시스템을 만들 때 많은 고민들은 결국 '구성요소 간의 역할과 책임을 어떻게 나눌 것인가'로 표현할 수 있습니다. 적게는 메서드, 클래스, 패키지 사이의 분담을, 크게는 jar로 배포되는 모듈이나 API로 통신을 하는 컴퍼넌트를 나누고 통신을 하는 방법을 고민을 하게 됩니다. 

 

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

 

개발,배포 방식

  • DB 스키마 관리
    • 개발환경에서는 Liquibase 로 관리
    • 운영환경에는 Liquibase에서 생성된 SQL을 사내 스키마 관리 시스템을 통해서 요청

인프라 기술 활용

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