2020년 1월 14일 화요일

Man of Month를 마치며

벌써 2020년 1월 14일이다.

19년의 마지막 달에 Man of Month라는 팀의 제도를 시작한다고 했었는데, 12월이 지나고 그 다음 달도 거의 절반이 흐른 것이다.

MoM을 시작하면서 하겠다고 계획했던 것들도 실제 한 것들과 비교해보니 많이 달라졌다.

먼저, 날마다 블로그에 글을 쓰겠다고 했는데 앞에 몇 개 올리긴 했지만 그 뒤로는 글을 올리지 못했다. 변명부터 해보자면, 글을 써보니 생각보다 시간이 오래 걸려서 이러다 글을 쓰고 남은 시간에 일을 하겠구나 생각이 들었다. 또 일을 진행하다 보니 절대적으로 시간이 많이 소모되는 작업들이 많아지고 있어서, 글을 쓰는 건 한 번에 하자는 생각이 들기도 했다.

새로운 공부를 하나씩 해보고 정리를 하는 게 기존 계획이었다. 하지만 그동안 시간이나 여유가 없어서 진행하지 못했던 것들을 해보는 시간이 되었다. 그래서 원래는 코드 베이스에 커밋을 거의 하지 않을 계획이었는데, 그 어느 때보다 많은 양의 변경을 진행하게 되었다.


201912 MoM을 시간대 별로 정리해보면 아래와 같다.

1주차

파트 On Call

파트 On Call 업무를 했다. 원래 MoM을 하게 되면 On Call 업무를 하지 않는 것으로 계획했었기 때문에 다른 누군가와 바꿀까 했지만, MoM을 하면서 On Call을 진행하게 되면 하는데 문제가 없을지를 체크해보는 관점에서 그냥 병행해보았다. 

참고로 라인에서 On Call 이란 일정한 기간 동안(보통 한 주 정도) 팀에서 담당하는 시스템의 안정성을 모니터링하고 문제가 발생하면 초동대처를 하며, slack 등의 대화 채널에서 문의에 답변하고, 라이브러리 업그레이드, 로그 추가/제거, E2E 테스트 안정성 확보 등의 다양한 작업들을 진행하는 제도이다. 

팀마다 작업의 범위가 좀 다르긴 하지만 대체적으로 해당 기간에는 다른 작업을 하기 어렵다고 생각하고 있었는데, 막상 MoM과 같이 해보니 예상대로 같이 하기는 힘들다고 판단 내릴 수 있었다. 따라서 On Call을 마치고 진행하는 On Call 공유 회의에서 이런 의견을 공유했고, 앞으로는 MoM과 On Call을 병행하지 않도록 내부 정리를 마쳤다.

JDK11 Upgrade

유일하게 조금이라도 블로그에 정리를 했던 작업이다. 국내에서 큰 규모로 자바를 사용하고 있는 회사들은 아마도 대부분 비슷한 상황일 거라 생각하는데, 과거에는 프로젝트의 의존성을 대규모로 업그레이드하는 작업 금기시되었다. 위험한 작업이라고 생각하기 때문이다. 오래된 라이브러리들을 업그레이드한다고 하면 말리는 분위기였다. 문제가 발생하면 누가 책임지느냐는 말을 듣기도 일쑤였다.

라이브러리를 업그레이드하는 문제가 그럴진대 Java Runtime을 바꾸는 문제는 얼마나 보수적이었을까?

당연하게도 훨씬 어려운 문제라고 생각했기 때문에 Java 버전이 그렇게 천천히 올라가던 시절에도, 다음 버전 혹은 다다음 버전이 나온 지 한참 지난 후에야 겨우 업그레이드를 고려하거나 아예 고려하지 않은 경우도 적지 않았다.

지금도 국내 업계 전반에서 볼 수 있는 최신 자바 버전은 8인 경우가 대부분이며 6도 종종 보이는 이유가 이 때문이라고 볼 수 있을 것이다.

하지만 최근에는 이런 분위기가 바뀌고 있다. 많은 Open Source 라이브러리들이 자체적인 의존성을 최신으로 유지하는데 많은 관심을 가지고 있고, 신규 Java 버전의 기능들도 좀 더 적극적으로 사용하고 있는 경향이 있다. 주류로 사용되는 Spring Framework 등에서도 신규 Java 지원이 빠르게 이루어지고 있기 때문에 예전처럼 각종 Framework가 의존성 업그레이드의 걸림돌이 되는 시기는 지났다고 생각된다.

무엇보다 Java의 버전 업그레이드나 변화의 수용 속도가 빨라지면서 버전 업그레이드로 인한 개별 편의성의 향상이나 성능의 이점이 점점 커지고 있다. 업그레이드에 소모되는 시간과 노력의 비용을 지불할 가치가 있다고 판단하는 경우가 많아지도 있는 것은 이런 환경의 변화 때문이 아닐까 생각된다.

LINE의 핵심적인 Back-End 시스템들을 대부분 자바로 되어 있다. 따라서 이러한 변화들을 잘 느끼고 있으며 실제로 많은 팀들이 의존성을 최신화하면서도 안정성을 확보할 수 있도록 다양한 보완책들을 추가해둔 상태이다. 

우리 팀에서도 비슷한 작업들을 하고 있었다. 최근 2년에 걸쳐서 중요한 라이브러리들을 업그레이드하고 좀 더 최근의 코드 스타일을 적용하는데 많은 노력을 들여서 이제 Java 업그레이드를 제외한 대부분의 작업을 마무리했다고 판단했다. 그래서 내가 MoM 기간이 이 작업을 시작하게 된 것이다.

막상 해보니 생각보다 크게 변경할 일은 없었다. 일단 Java11이 Java8에 대해서 어느 정도 수준의 하위 호환을 가지고 있었기 때문이다.  버전 업그레이드에 문제가 되는 라이브러리나 코드를 수정한 후 며칠 만에   Java11을 기반으로 프로젝트를 구동할 수 있었다. 향후 인프라팀과 협의해서 운영 서버들에 테스트하는 작업들과 실제 배포하는 작업들이 남았지만 계획을 세워서 진행하면 문제가 없을거라 생각되었다. 

사실, 더 큰 작업은 실제 Java11이 운영 시스템에 올라간 이후에 코드 베이스에 최신의 코드 트렌드를 적용하는 부분이다. Java8로 업그레이드했을 때도 팀이 새로운 개발 방식에 적응하고 이를 코드에 제대로 반영하는데 꽤 오랜 시간이 걸렸던 기억이 있기 때문에 Java11으로 Runtime을 변경하는 사전 작업이 끝난 후에도 코드 베이스를 변경하는 사후 작업에는 많은 시간과 노력이 필요할 것이라고 판단하고 있다.

2~3주차

JUnit5 Upgrade

위에서 Java 업그레이드를 위해서 대부분의 라이브러리 업그레이드 작업을 마쳤다고 이야기했지만 손도 못 댔던 부분이 하나 있었다. 바로 JUnit5로 업그레이드하는 것이다. Java 업그레이드는 앞으로 문제가 될 상황에 미리 대응하는 관점에서 진행하는 것이었다. 하지만 JUnit5 업그레이드는 프로젝트의 의존성들을 최신으로 유지하기 위해서는 지금이라도 꼭 해야 할 작업이라고 판단해서 진행하는 작업이었다. 벌써 최신 Spring Framework에서는 JUnit5를 기본으로 사용하고 있었고 이로 인한 문제가 확인되고 있는 상황이었다.

필요성은 꽤 오래전부터 논의되었고 이슈로도 생성해서 관리했는데 작업이 소용되는 시간과 노력이 많은 것으로 예상되고 코드 변경도 꽤 있을 것이라 엄두가 나지 않던 작업이었다. 하지만 다행히도 12월 중순 이후로 신년 대응의 일환으로 코드 프리징이 진행되고 있어서 몇 주간 릴리스가 없을 예정이었다. MoM 때 하기에는 적당하는 생각이 들었다.

먼저 간단하게 프로젝트 간 의존성이 가장 적은 모듈을 골라서 작업을 진행했다. 다행히 JUnit5는 Vintage Engine이라는 JUnit4 기반의 코드를 사용할 수 있게 해주는 모듈을 제공하기 때문에 단순한 테스트 코드는 문제없이 동작하는 걸 확인할 수 있었다. 하지만 @Rule 등의 annotation이나 JUnit에 적용된 assertThrows나 Parametized 등의 기능의 사용을 위해서는 테스트 코드 변경이 불가피한 상황이었기 때문에 이번 기회에 모든 테스트 코드를 JUnit5 기반으로 변경하기로 결정했다.

PowerMock, JUnitParams 등의 의존성을 제거하고 필요한 Extension 등을 만들거나 찾아서 사용하고 최신 버전의 Mockito 사용으로 인해서 발생하는 문제들을 제거하고, JUnit5 기반으로 코드 베이스를 변경하는데 상당한 시간이 걸렸다. 우여곡절 끝에 Develop에는 머지 할 수 있었고, 얼마 뒤 Master에도 머지 할 수 있었다.


4~5주차

Checkstyle 적용

JUnit5 적용 작업을 하면서 곁다리로 간단하게 적용해보려고 했던 checkstyle 적용 작업이 JUnit5보다 더 많은 변경을 만들게 되었다.

사내에 공통 Coding Convention이 있고, IntelliJ 용으로도 Code Style과 Indent 파일이 있기 때문에 이를 사용하고 있었지만, 시스템에 코드의 스타일과 관련된 부분을 강제는 checkstyle을 사용하지 않았다. 몇몇 팀들은 쓰고 있었지만 우리는 사용하지 않고 있었다.

최근에 여러 이유로 팀에 사람도 많아지고 다른 부서에서도 PR이 종종 등록되고 있는 상황이라서 checkstyle 적용의 필요성을 느끼고 있었는데, JUnit5 업그레이드를 하기 때문에 어차피 코드 변경이 많을 테니 이때 하는 게 좋겠다는 생각으로 시작하게 되었다.

사내의 다른 팀들이 사용하는 checkstyle을 사용하려고 했으나 오래된 버전이라 사내 오픈소스 프로젝트인 Armeria에서 사용하는 checkstyle을 우리 기준을 반영하여 조금 수정 후 사용하는 것으로 최종 결론을 내렸다.

적용은 크게 두 가지로 나누어서 진행하였다.

첫 번째는 환경 구성이다. checkstyle 설정을 기존의 indent나 code style에 맞춰서 수정하고, IntelliJ 플러그인 설정,  Jenkins에 PR 용 checkstyle 작업을 생성해서 Github PR Hook에 등록 등이 이에 해당한다.

두 번째는 코드를 수정하는 작업이다. 간단하게는 Copyright를 추가하거나 unused import를 제거하는 작업에서 JavaDoc의 빠진 구성요소를 추가하는 작업이나 변수나 메서드의 Access Modifier를 적절하게 변경하는 작업까지 프로젝트의 모든 파일을 기준에 맞춰서 작업해야 했다.

두 번째 작업은 혼자서 하기에는 일이 많고, 어차피 팀원 모두가 익숙해져야 하는 부분이라 생각되어서 팀원들 중에 가능한 사람들은 모두 함께 변경 작업을 진행하기로 했다. 모듈 단위로 Jira Issue에 sub-task를 만들어서 등록했으며 각자 여러 개씩 맡아서 변경을 진행했다. 모두의 힘으로 생각보다 빠른 시간 안에 checkstyle 기준으로 통과하는 PR을 만들 수 있었다.



 MoM을 마치며

개발을 하면서 처음으로 키보드를 칠 때 손가락이 저릿한 느낌을 받았다. 아마도 checkstyle 작업을 하던 날이었다. 그날 마침 일찍 출근하는 날이라 아침 7시에 와서 작업을 시작했는데, 아이를 픽업할 필요가 없어져서 밤 10시까지 일을 했던 날로 기억한다. 코드 변경 작업이 단순하기는 한데, 나름 재미있어서 집중해서 아침부터 저녁까지 계속 키보드를 쳤던 것으로 기억한다. 처음으로 손가락 끝이 저릿저릿 한 느낌을 받을 수 있었다. 

MoM 기간을 대표하는 경험이었다. 너무 깊은 고민 없이 주어진 문제들을 처리하는 시간을 가져볼 수 있었다. 문제는 명확했고, 해결책도 단순했다. 문제를 해결하면 그 결과를 바로 확인할 수 있었고 일을 나누면 통합의 부담 없이 협업이 가능했다. 결과물이 측정 가능하고 직관적으로도 확인할 수 있었기 때문에 일에 대한 성취감을 바로바로 느낄 수 있었다. 손가락이 저릿할 정도로 타이핑을 하고도 그만둘 줄 몰랐던 게 그런 이유가 아니었나 생각한다. 

좋은 경험이었고 앞으로의 일에도 적용할 만한 교훈을 얻을 수 있었다.


2019년 12월 6일 금요일

20191206 On Call


오늘은 자잘한 업무를 처리하느라 마이그레이션 작업을 진행하지 않으려고 한다.

MoM 진행하는 동안 업무일 기준으로 하루에 하나의 포스트를 작성하고자 하기 때문에 오늘은 회사의 제도 하나를 소개해보고자 한다. 마침 이번주에는 내가 On-Call이라 활동을 진행중이기도 하다.

LINE 서버 개발 부서들은 On-Call이라는 시스템을 가지고 있다.

On-Call 사전적인 의미를 찾아보면 다음과 같다.

If a worker such as a doctor is on call, he or she is available to work or make official visits at any time when needed:



간단하게 이야기해보면 필요한 상황에 바로 업무 대응을 있는 준비상태를 의미한다. 당직이나 당번이 이에 해당한다고 있고, 백엔드 개발자들이 주로 사용하는 의미는 업무시간 외에 서비스에 문제가 발생하거나 알람 등이 발생할 이를 먼저 판단하고 처리하는 역할 정도로 있을 같다.

그렇다면 팀에서는 어떻게 제도를 운영하고 있을까?

일단 한명이 1주일간 On-Call 진행한다. 그리고 아래의 사항들을 수행하고 있다.
  • 매일 아침 Server Daily Report 확인 하고 문제가 의심되는 로그를 분석해서 문제를 해결하거나 위임한다
  • 문자, 메일, 메신저, 슬랙으로 전달되는 시스템 WARN, ERROR, FATAL 로, 각종 알람을 확인하고 간단한 분석을 대화방에 공유한다
  • 날마다 쌓이는 다양한 로그들을 확인 로그의 레벨 조절하거나 메시지를 개선하는 등의 로그 개선 작업을 진행한다
  • 슬랙의 사내 문의 채널에 질문이 등록되면 먼저 확인해서 처리 혹은 위임한다

별로 역할이 조금씩은 다를 있겠지만 LINE 서버 개발 부서들은 대부분 비슷한 제도를 수행하고 있다고 있을 것이다.

다양한 업무를 하는 것으로 있겠지만, 핵심은 다음의 가지다.
  1. On-Call 기간 동안 다양한 경로로 시스템의 안정성을 모니터링
  2. 모니터링 결과 이상 신호가 감지되면 1 확인 처리 공유

24시간 서비스를 제공하는 메신저의 특성상 다양한 분서들이 모니터링을 진행하는데 서버 개발자들이 직접 봐야할 부분들을 서로의 시간을 나누어서 진행함으로써 나머지 인원들이 개발과 휴식에 집중할 있도록 한다고 있다.

문제를 파악하는 것과 그에 대한 1 조치를 취하기 위해서는 시스템의 전반을 이해하고 어떤 신호들이 존재하는지, 상황에 따른 대처 방법들은 어떤 것이 있는지를 알아야 한다. 따라서 팀에 합류한지 얼마 되지 않은 인원들은 기존 멤버와 함께 On-Call 진행하는 등의 과정을 통해서 적응에 필요한 시간을 가지고 있다.

On-Call 업무에 대한 회고를 정기적으로 진행하면서 효율성을 높이기 위한 방법들을 찾고 있으며, 만들어둔 On-Call 매뉴얼도 지속적으로 개선하고 있다.

20191205 Java11 마이그레이션 2일차


어제에 이이서 진행하는 프로젝트의 SDK 8에서 11으로 마이그레이션 하는 작업을 진행중이다.

어제 실패하는 testcase 수정하기 시작했고 하나씩 수정을 진행하고 있다.

오늘 수정한 첫번째 케이스는 내부적으로 사용하는 LanguageUtils라는 클래스의 Unittest인데, 자바의 Locale 활용해서 en-US 등의 문자열을 로케일 정보 등으로 변환하는 작업을 돕는 역할을 하는 클래스이다. 사용할 locale 문자열이 표준에 맞지 않은 경우들이 있기 때문에 이를 해결하는 휴리스틱한 코드들이 있고 이에 대한 테스트 케이스도 존재하는데 자바의 버전이 올라가면서 Locale 클래스에서 지원하는 언어나 국가들이 많아지게 되어 없는 Locale 이라고 생각하고 테스트 케이스를 만들었던 것들이 실패하게 것이다.

예를 들어 ka라는 언어 코드를 가진 Locale 8버전에는 없었으나 11버전에는 추가되어서 없을 경우를 가정한 테스트가 깨지는 등의 문제가 있었다.

테스트케이스 수정은 간단하게 진행할 있었다. 존재하지 않는 코드들을 찾아서 원래의 의도에 맞게 테스트를 통과할 있도록 수정했다.


나머지 테스트케이스들에는 문제가 없었기 때문에 여기까지 작업한 내용을 기반으로 Pull Request 만들어서 등록했다. 앞으로 필요한 작업은 베타 서버 한대에 배포해서 동작을 장기적으로 확인하는 일과 다른 모듈들도 제대로 동작하는지 검증하는 일이다.

2019년 12월 4일 수요일

20191204 Java11으로 가는 길


Java11으로 가는

사내에서 Java8에서 11으로 업그레이드 성능 향상에 대해 공유된 적이 있다. 유의미한 수준의 성능 개선을 확인할 있었다. 동안 Java 업그레이드 관련해서 언젠가 해야하겠다는 정도만 생각하고 있었는데, 실행해야겠다고 생각하는 계기가 되었다.

무엇보다 차세대 LTS 버전이 존재하는데 계속 이전 세대의 JDK 유지하고 있으면 유지보수에도 불리하기 때문에 업그레이드가 필요하다는게 팀원들 대부분의 의견이기도 했다.

Oracle Java SE Su ort Roadma 
Release 
6 
7 
9 (non-LTS) 
10 (non-LTS) 
It (LTS) 
12 (non-LTS) 
13 (non-LTS) 
GA Date 
December 
2006 
July 2011 
March 2014 
September 
2017 
March 2018 
September 
2018 
March 2019 
September 
2019" • 
Extended Support 
Premier Support Until 
December 2015 
July 2019 
March 2022 
March 2018 
September 2018 
September 2023 
September 2019 
March 2020 
Until 
December 2018 
July 2022 
March 2025 
Not Available 
Not Available 
September 2026 
Not Available 
Not Available 
Sustaining 
su 
Indefinite 
Indefinite 
Indefinite 
Indefinite 
Indefinite 
Indefinite 
Indefinite 
Indefinite


작업을 시작하기에 앞서 사내의 사례를 찾아봤는데, LINE Shop 케이스가 먼저 보였다



1 전에 작업 자바 유저 그룹에서 발표까지 내용이 있었고, 사내의 위키에는 자세한 내용들도 있어서 좋은 참고 자료가 되었다. 라인 메신저에 관련된 백엔드 대부분이 비슷한 인프라를 사용하고 있고 내부 구조도 거의 동일하기 때문에 shop 케이스는 우리에게도 수정 없이 적용할 있을 것이라 판단했다.

일단 간단하게 마이그레이션 작업을 측정하기 위해서 다음의 사항을 진행해보기로 한다.
  1. 메인 개발 장비인 IMac 업무 대응에 사용해야 하므로 Macbook Pro JDK11 설치한다
  2. 실패할 경우를 고려해서  SDKMAN으로 설치를 진행
  3. 로컬에서 프로젝트를 빌드할 있도록 작업
  4. unittest 모두 성공할 있도록 작업
  5. Integration Test 모두 성공할 있도록 작업
  6. 프로젝트가 정상적으로 기동하도록 작업
  7. E2E 테스트가 모두 성공할 있도록 작업

일단 위의 내용들이 모두 성공하면 마이그레이션이 가능하다고 판단할 있다고 생각한다. 이후 필요한 작업들은 마이그레이션 계획을 수립해서 팀에 공유하고 Beta -> Staging -> Release 환경으로 차근차근 진행하는 것이 필요하다고 생각한다.


첫날에 진행한 작업은 다음과 같다.

JDK11 설치 개발환경 세팅

먼저 맥북에 JDK11 설치했다. SDKMAN 이전에 설치해두었기 때문에 `sdk use` 명령으로 11 기본으로 사용하도록 하였고, IntelliJ에서 프로젝트 기본 SDK 11 사용하도록 수정했다.

컴파일 문제 해결

컴파일 과정에서 먼저 확인된 문제가 @Resource 어노테이션의 클래스를 찾을 없다는 문제였다. 해결방법을 찾아보니 dependency 추가해줘야 한다는 것을 있었다.

javax.annotation:javax.annotation-api

위의 dependency gradle 적당히 넣어주고 문제는 해결 있었다.

Unittest 문제 해결

Unittest 실행해보니 열개 조금 넘는 실패가 있었다. 첫번째로 문제를 확인한 것은 powerMock에서 에러가 발생하는 것이었는데 bytecode 조작에 사용되는 ASM 6.x 버전을 JDK11에서 지원하지 않아서 발생하는 것이었다. 해결책은 두가지가 있는데, 첫번째는 ASM7 사용하도록 하는 것이고 두번째는  powerMock을 제거하는 것이다.

예전부터 powerMock 필요한 상황은 아니라고 생각하고 있었기 때문에 이번 기회에 Mockito JUnit 기능들을 사용해서 powerMock 대치하기로 결정하였다.


향후 진행 과정을 여기에 하나씩 정리해볼 계획이다.




Man of Month를 마치며

벌써 2020년 1월 14일이다. 19년의 마지막 달에 Man of Month라는 팀의 제도를 시작한다고 했었는데, 12월이 지나고 그 다음 달도 거의 절반이 흐른 것이다. MoM을 시작하면서 하겠다고 계획했던 것들도 실제 한 것들과 비교해보니...