특수화와 범용성

처음 프로그래밍을 시작한 시점부터 얼마나 많은 코드를 작성해왔는지, 그 코드들 중 얼마나 많은 코드가 아직도 실행되고 있는지를 알 순 없지만, 내가 짠 꽤나 많은 코드가 아직 현역일 것이다.

그 과정에서 자주 고민하게 되는 이슈 중 하나는 바로, 특수화와 범용성이다.

혹자는 특수화 자체를 나쁘다고 규정 짓곤 하지만, 투입되는 코스트가 작아지고 예외가 적어질 수 있다는 점에서 특수화는 분명히 큰 장점이 있다.

서두에 왜 내가 짠 코드가 아직도 현역일 것이란 얘기를 했냐하면, 범용성이란 내가 작성한 코드가 현역인지 여부보다는 내가 작성한 코드가 얼마나 더 수정되거나, 재사용 될 것인가에 의미가 있기 때문이다.

심지어는 수정이 되는 과정에서나, 수정이 되기 위해 검토되는 과정에서 마저 특수화가 더 중요한 가치일 때가 많다.

범용성을 위해 모호한 네이밍, 모호한 사용법, 열려있는 기능이 코드 분석을 어렵게 할 수 있는 요인이 되기 때문이다.

물론 범용성 자체가 나쁘단 얘긴 절대 아니다. 범용성 자체는 중요한 가치중 하나다. 작은 수정에도, 기존 코드에 큰 수정을 가해야해 리스크를 크게 가져가야 하는 상황은 분명히 잘못됐다. 하지만, 이미 오래 전에 open-close 원칙으로 확장에 유연하고, 기존 동작에 리스크 없게끔 범용성을 갖추는 방법이 고안되어있으며, 20-80 법칙등을 근거로 자주 사용될 때는 범용성을, 자주 호출되서 실행시간의 80%이상에 포함되는 코드는 특수화를 통한 성능 향상과 오류율 감소를 얻어내야 한다.

작성하는 코드가 해야 할 역할에 따라서 다르다는 점은 매우 중요하다. 재사용되지 않을 기능, 특수한 상황만을 위한 어쩔 수 없는 비지니스 특화 코드 등에 범용성을 위한 코스트를 들이는 것은 낭비라고 볼 수 있다.

심지어는 어설픈 범용성으로 인해, 기능을 재사용하기 위해 많은 수정이 다시 필요한 경우도 존재한다는 점도 무시할 수 없다. 모든 시나리오를 위한 범용성을 갖췄을리 없기 때문에, 새로운 요구사항 혹은 새로운 사용법이 필요해졌을 때가 대비되어 있지 않을 가능성이 높기 때문이다.

다시 고칠일이 없는 코드에 범용성을 위해 몇일을 소모하는, 휴먼 리소스 낭비마저 합리화 될 순 없다. 모두에게 시간은 평등하고, 가치 있기 때문에, 오버 엔지니어링 하지 않고 효율성도 고려하는 것이 좋은 엔지니어이기 때문이다.

대부분의 경우에는, 나쁘지 않은 구현으로 기능을 개발 한 뒤, 자주 사용되거나, 요구 사항이 확정 되거나, 피드백을 반영해서 완성하는 것이 합리적일 때가 많다.

내 경험상으로는 범용성을 위해서 들이는 코스트도, 특수화를 위한 결단과 판단력도 모두 빠른 프로토타이핑 혹은 방향성을 확인하기 위한 예광탄으로 검증 된 뒤 고도화 하는 것이 합리적인 경우가 많았다.

대부분의 요구 사항은 결과물을 보기전까지 모호함과 결함을 내재하고 있기 때문이다. 이 것이 애자일에서 주로 언급하는 빠른 이터레이션이다. 그 과정을 좀 더 명확해진 설계로 이어지고, 곧 좀 더 특수화, 범용화 하기 쉬워진다.

재작업을 비효율이라 여기는게 개발자들의 공통적인 생각인데, 범용화를 위해 쓰이는 시간이 낭비가 될 수 있다면, 이 역시 비효율로 여겨져야 한다고 생각한다.

범용화를 위해 확장성도 있고, 안정성도 잡는 룰을 갖추고, 그 룰의 시행착오를 갖추고 적용하기 위해선 아주 아주 많은 코스트가 들지만, 그만큼 가치있다.

그럼에도 거기에 들어가는 시간이 과다하다면, 엔지니어도, 엔지니어와 협업하는 다른 사람들도 납득시키기 어렵다.

난 코드 작성시의 범용성을 최우선 가치로 여기는 것은 옳지 않다고 생각한다. 범용성이 필요한 상황은 분명히 존재하나, 항상, 매번 중요한 가치로 여겨지기엔 들어가는 코스트가 너무 크고, 그 코스트가 효율성을 얻기 어려운 상황을 나는 매우 자주 접했기 때문이다.

빠른 구현을 통해 방향성과 타당성을 검증하고, 그 이후에 범용성을 선택할지, 특수화를 선택할지 상황과 해야 될 일에 맡게 판단하는 것이 합리적인, 실용적인 프로그래머가 아닐까?

2018년 회고

다사 다난한 일이 꽤 자주 있는 편인지라, 평소와 같았다 볼 수 있는 한해가 또 저물어 간다.

과연 나에겐 어떤 일들이 있었고, 어떠한 생각들을 가지고 살았으며, 내년엔 무엇을 하고 싶은지 간단히 정리해보고자한다.

블로그

한해 동안 꽤나 많은 글을 썼다. 10월 초 이후의 글을 못썼음에도 35개의 글을 썼다.

사람에 대한 글, 기술에 대한 글 다양한 종류의 글을 썼다.

생각나는 주제에 대해서 짧게 기록 해둔 뒤, 시간이 날 때 글을 마무리해서 올리곤했다.

업무 연관한 생각들을 정리했고, 스터디를 진행하면서, 식사 중에 나누던 대화 주제, 최근에 읽은 기술 서적에 대한 이야기 등 많은 이야기를 쓰고자 의도했다.

작년과 올해 실무 언어를 자바를 사용하면서 새로 접하는 환경이 많다보니, 할말도 많아졌던 것 같다.

굳이 의도적으로 더 많은 글을 쓴다거나, 양질의 글을 쓰기 위해 한달에 한번 쓴다거나 이럴 생각은없고 지금처럼 쓰되, 스크린샷이나 샘플 소스 코드를 좀 더 늘리는 방향으로 계획하고 있다.

기술적 관심사

웹 개발을 좀 더 비중 있게, 실무로 일하고 있다. 작년에도 하반기에는 웹개발을 했으나, 올해는 프론트엔드도 진행하고 있으며, public 서비스이다보니 일의 갯수보다는, 일의 질에 관심을 가지고 있으며, 자바를 그냥 쓰다가, 조금 더 제대로 쓰기 위한 여러가지 계기도 마련됐고,익숙해지고 있다.

또한 Vue.js를 통해 frontend 실무를 처음으로 진행하게 됐고, 좀 더 웹에 대한 이해도가 높아지고 있는 것을 느끼고 있다.

개인적으론 최근 몇달간은 데이터 처리 관련 업무를 진행할 수 없었는데, 개인적인 시간을 할애해서라도 이어가고 싶다.

웹 & 플랫폼 개발

자바와 웹을 접하면서, 가장 많이 깨닳은 것중 하나다.

게임 개발은 일정 양 이상의 컨텐츠를 만들어야 한다. 프레임워크에 의존한 개발을 하는 경우도 드물고, 그 프레임워크마저 그 게임이 추구하는 몇가지 가치에 의해 뜯어 고쳐서 적용하게 될 만큼, 그 프로젝트 만을 위한 코드가 많이 생산된다.

웹 개발은 반복되는 문제들이 꽤 많고, 이 문제들에 대한 오픈 소스 솔루션들이 여럿 존재한다. 내가 직접 생산 하는 코드 양은 적지만, 잘 조합하고, 내가 만드는 코드가 솔루션이나 프레임워크가 제시하는 방향과 유사하게끔 구현하고 관리하는 것이 중요하게 받아들여 지더라.

토이 프로젝트

rails를 통해 웹을 접하면서 느꼈던, 잘 가져다 써서 빠른 구현을 통해 컨텐츠에 집중할 수 있는 환경을, 자바 & 스프링을 익히면서, 이를 바탕으로 업무를 진행하면서 좀 더 익숙해졌다.

다른 프로젝트를 forking하기도 하고, 업무나 개인적인 공부를 통해서 익힌 것들을 통해 간단한 프로젝트들을 진행했다.

다양한 국내 커뮤니티를 크롤링해서 mongodb에 저장하고, 이를 보여주는 게시판, 데스크탑 notificator, slack notificator (line notify로 만들었다가 수정) 등을 만들어 사용 중이며, riot api를 이용해 등록한 친구의 게임 시작/승/패 정보를 전달해주는 loltracer를 만들어서 지인들의 플레이를 추적하는 데에 쓰고 있다.

개인적인 관심사와, context switching에 쓰이던 코스트를 줄이는 것을 목표로 삼아 개발했고, 이를 통해 여러 게시판을 돌아다니는 데에 쓰는 human cost (polling)을 줄이는 데에 성공했다.

내년에는 오래 전부터 계획중이던 다른 토이 프로젝트를 구현 하기 위해, 우선 순위와 관심사를 조정하고 있다.

개인적인 이야기

재입사를 했다. 이런 케이스가 꽤나 있는편이라, 굳이 특별할 건 없지만, 내 경우는 처음이라 많은 감정이 느껴졌다.

특히 같은 회사 내에서 게임 개발 부서에서, 웹 & 플랫폼 개발 부서로 재입사를 하는 과정과, 실무를 진행하는 과정에서 아주 많이 다른 것들을 느끼고 있다.

게임 개발도, 웹 개발도 암묵적인 룰들이 많은데, 아직은 그런 것들 서로간의 차이와 장단점을 받아들이고 이해하고, 흡수하는 데에 주력하고 있다.

DB를 바라보는 관점, 스케일 아웃에 대한 마인드, 중요한 메트릭의 종류, 일하는 방식, 중요한 업무적 가치 등 작을 줄 알았던 차이가 예상보다 많이 커서 놀랐다.

그래도 사람은 적응의 동물인가보다. 4달여 지난 지금 많이 적응했지만, 아직 배워야 할 것도, 익숙해져야 할 것도, 짬짬이 공부해야 할 것들도 많다.

그렇기 때문에 내년에도 할 것들이, 성장 할 수 있는 꺼리들이 많이 있다고 생각한다.

2019년 목표

위에서 조금 언급한 것들도 있지만, 정리하자면 다음과 같다.

  • 영어 공부
  • 블로그 글에 코드 샘플과 이미지를 좀 더 많이 첨부하고, 읽기 쉽게끔 문장 가다듬기
  • 토이 프로젝트 활성화
    • 지금 처럼 컨텐츠를 만들어 사용하는 방향도 좋지만, 좀 더 많은 사용자가 이용할 수 있는 패키지 개발도 검토 중이다.
  • 강아지 산책 시간 늘리기
    • 짧게 자주! 특히 주말에!
  • 독서 시간 늘리기
    • 인문 서적 비율을 늘려야 할 때 인 것 같다.
    • 소설도 조금 더 늘리고 싶지만, 시간이 될 까 모르겠다.
  • 데이터 처리/분석에 대한 좀 더 심도 있는 공부와 적용.
    • 언젠간 업무적으로 이어갈 기회로 이어질 수 있도록 조금 더 노력해둬야 겠다.

얼마나 이뤄낼 수 있을지는 모르겠으나, 장황하고 모호하게 썼으나 내 목표치 자체가 딱 끝맺음이 있는 것들보다는 꾸준히 지킬 수 있는 범주에 두는 것이 좋다는 것은 살아오면서 느꼈기 때문에 그렇게 계획하고자 했다.

내년 한해엔 가능한한 많은 것들을 지키고, 이뤄 낼 수 있는 한해가 되면 좋겠다.

MySQL LIMIT + Offset 쿼리 성능 문제

페이징 처리시 자주 사용되는 Offset. 단순히 쿼리로 OFFSET을 수행하면 성능 저하가 일어난다.

offset 사용시 성능 저하 이슈에 대해 검토와 관련 글들

이를 확인하기 위해 DB에서 직접 쿼리를 날려 보자 (속도 차가 나는 것은 실제 사용 쿼리보다 좀 더 단순한 쿼리로 비교했기 때문임) 30만건 이상의 데이터를 넣어둔 테이블에서의 결과임을 밝혀둔다.

SELECT * FROM 테이블
order by project_name asc
LIMIT 10 OFFSET 300000;

default_limit_offset

3.2초
Full Table Scan이 일어남 (전체 데이터 건수에 대한 접근)
그리고, 그 데이터들을 모두 정렬하는 과정의 코스트가 많이 발생함.

SELECT * 
FROM 테이블 as p
JOIN (SELECT 키값 
    FROM   테이블
    ORDER  BY 정렬컬럼 LIMIT 300000, 10) AS t ON t.키 = p.키; 

tuning_limit_offset

0.641초
Full Index Scan이 일어난 뒤, 인덱스를 정렬함.
정렬된 데이터를 기준으로 Unique Key Lookup 기반의 Nested Loop 처리로 인한 성능 향상

인덱스 구조 관련 팁

  • LIMIT OFFSET 에서 사용하는 Sorting 값이 있다면, 해당 값을 Clustering Index로 잡으면 성능 이득을 더 크게 볼 수 있다.
  • Sort 과정을 건너뛰어도 되는 만큼 처리할 데이터양이 많거나 클수록 차이가 나는 부분이니, 페이징할 데이터 양이 큰 경우 검토할 여지가 있다.

Ubuntu vs CentOS

종종 언급했지만, 나는 자의적으로 서버 프로그래머가 된 케이스는 아니다.

반 강제 서버 프로그래머로 전향한 2006년 당시만해도 대다수의 게임 개발은 윈도우 기반의 온라인 게임이었다보니, 당연히 게임 클라이언트 개발도 윈도우가 지배적이었다.

더군다나 게임 서버 개발자 중 반절 이상은 클라이언트 개발을 하다가 인원 부족이나 전문 인력 부족으로 전향한 나와 같은 케이스가 많다 보니 클라이언트 개발시에 그리고 평상시 자주 사용해 익숙하던 윈도우 환경에서 서버를 구축하기 시작했다.

아무래도 공통된 유틸리티성 코드 활용을 위함과 학습 코스트, 운용 코스트 등을 고려한 측면에서의 영향도 큰데, 서버와 클라이언트의 많은 코드를 공유하게 되면 커버리지도 공동 확보되는 경향도 있어서 일 것이다.

나 역시 클라이언트에서 전향한 사례였다 보니 윈도우가 훨씬 익숙했고, 윈도우 서버로 게임 서버를 꽤나 긴 시간 개발해왔다.


그러던 중 리눅스를 실무에 사용하게 된 계기는 윈도우에서 production이 불가능에 가깝던 ruby on rails를 쓰게 되면서 였다.

학습용으로 rails를 배워나가면서, 로그 처리 웹서버를 구성했었다. 간단한 기능이나 rails의 기본 기능들 까지는 동작하는데, 다수의 패키지가 윈도우에서 안도는 것들 투성이라서 선택지가 없었다.

같은 이유로 이후에 스타트업에서 ruby on rails를 이용한 웹 서버로 게임 서비스를 해야 될 때도 어쩔 수 없이 리눅스를 써야 했다.

윈도우 서버로는 production 레벨로 올릴 수 없기에, 여러가지 리눅스를 검토하기 시작했다.

검토하다 보니 Ubuntu와 CentOS로 좁혀졌는데, 이는 다수의 클라우드 서버 및 물리 서버 호스팅을 위한 검토과정에서 공통적으로 지원하는 리눅스 계열 OS는 이 둘 뿐이었고, 왜 그런가를 살펴보았다.

CentOS

  • RHEL을 지연 반영하는 안정성 높은 OS
    • 반대로 버그 패치도 지연 반영되는 단점.
  • RPM(Redhat Package Manager)를 이용한 쉬운 설치/제거.
    • 의존성 패키지 문제를 유발하는 원흉이 되기도 함.
  • 밸런스 있는 발전 속도와 혁신 (RHEL의 기조를 그대로 따라 감)
  • 기술 지원이 쉽지 않음.
    • 필요할 경우 RHEL을 쓰는게 낫다고 여겨짐.
  • 레드햇 계열 배포판 비교

Ubuntu

  • LTS를 통한 장기 지원, 단기 유지되지만 새로운 시도를 하는 일반 버전 동시 지원.
  • 다수의 사용자 커뮤니티 확보
  • 데비안 기반의 신뢰도
  • 데비안을 지연 반영하는 높은 안정성과 빠른 변화를 추구하는 우분투의 방향성의 밸런스
    • 대신 빠른 버그 수정 및 대응이 강점.
  • 캐노니컬의 영업력으로 인한 높은 하드웨어 호환율

우분투는 데비안의 안정성을 일부 취하면서, 빠른 변화와 새로운 패키지나 규칙 변화에 능동적인 편에 속한다. LTS 버전이라 할지라도 데비안이나 CentOS보단 불안정할 수 있지만, 그럼에도 최신 패키지나 새로운 라이브러리들을 가져다가 시험적인 혹은 프론티어 적인 시도를 하기 좋다고 볼 수 있다.

CentOS의 경우 안정 지향의 RHEL의 지연 반영이라는 점에서 큰 인기를 끄는 편이고, 왜 그런지 모르겠으나 같은 포지션의 데비안은 국내에선 상대적으로 덜 선택되는 편이었다.

이 글을 정리하면서 궁금해서, 여러 글을 읽게 됐고 각 OS별 위키를 자세히 읽고 확인하게 됐는데, 해외에서의 점유율은 Ubuntu, Debian, CentOS가 3톱을 구성하고 있었고, 그 중 Ubuntu가 압도적인 수치 (37.5%)를 차지하고 있음을 알 수 있었다.

2017년의 웹 기술: https://w3techs.com/blog/entry/web_technologies_of_the_year_2017

os_of_the_year_2017

리눅스 배포판 별 점유율: https://w3techs.com/technologies/details/os-linux/all/all

2018_september_percentage_of_websites_using_linux

2018_september_subcategories_of_linux

글 제목에서도 알 수 있듯이, 나는 CentOS (+RHEL)과 Ubuntu (+Debian) 의 구도를 유지하고 있을 거라 생각했는데, 통계 사이트가 정확한 수치를 반영하기 어렵다는 것을 알지만, 그럼에도 꽤나 큰 차이는 충격적이었다.

이런 큰 수치차이는 패키지 매니저의 특징과 관리 이슈, 안정성에 대한 시선에 따른 차이지 않을까 싶다. 우분투 데비안과 CentOS 사이의 균형을 잘 찾았다고 인지되고 있는 듯 하고, 그 정점에는 LTS를 통한 안정적 버전에 대한 신뢰도 있는게 아닐까 싶었다.

배포판 별 위키

리눅스를 능숙하게 쓴다고 감히 얘기할 수 있게 된 시기는, 각 OS별로 수많은 어플리케이션을 다뤄보고, 운용해보고, 이슈를 겪어보고 나서야 말 할 수 있었다.

어플리케이션을 구동하기 위한 패키지 관리, 직접 컴파일해서 사용, 패키지간 충돌 해결 등의 다양한 문제와 함께 익숙해져감을 몸소 느낄 수 있었고 말이다.

그리나서야 CentOS와 Ubuntu의 장단점에 대해서 얘기할 수 있을거란 생각이 들어 이 글을 쓰기 시작했고, 분명히 CentOS를 좀 더 지지하기 하려 했었으나, 통계와의 오차는 당황 스러우면서도 매우 흥미로웠다.

또한 내 개인적 경험으로 인한 편견인지, 실제로 그걸 감안하고도 밸런스 좋은 OS인 Ubuntu가 인기 있는 것인지 알고 싶어졌다.

한편으로는 동시에 서버 OS 배포판 별 통계나, 배포판 별 역사, 배포판 별 특징 등에 대해서 좀 더 잘 알 수 있게 된 계기가 됐다는 생각도 든다. 그리고 데비안을 써보고 싶어졌다.

Vue.js 참고 자료

가이드

사례

디버깅

사용법