프런트엔드 개발자로서의 1년을 돌아보며


NHN FE개발랩에 신입사원으로 입사하여 프런트엔드 개발자로 일한 지도 거의 1년이 되어간다. FE개발랩에 오기 전까지 웹 개발을 한 번도 해본 적이 없어서 걱정을 했었지만 랩의 신규 입사자 교육과 브랜드사이트 개편, 이미지 에디터 프로젝트를 통해 많은 성장이 있었다고 생각한다. 이 글을 통해 각 커리큘럼들을 경험하면서 겪었던 어려움과 해결 사항 그리고 이를 통해 배우고 느낀 점을 공유한다.

신규 입사자 교육

FE개발랩은 신규 입사자가 오게 되면 커리큘럼이 정교하게 짜여 있는 교육을 진행한다.

책 리뷰

가장 먼저 자바스크립트 책을 학습하고 나서 해당 내용에 대해 강의를 하는 책 리뷰를 진행한다. 따라서 자바스크립트를 모르더라도 충분히 학습을 할 수 있다. 기초부터 정교하게 다루는 책이다 보니 자바스크립트를 깊게 학습할 수 있어 좋았다. 예를 들어 왜 IE9 이하에서 참조에 null을 저장해 참조를 끊어 가비지 컬렉션이 회수할 수 있도록 해야 하는지 자세히 설명해 준다. 답은 익명 함수에서 참조를 계속 유지하고 있어 참조 카운트가 줄어들지 않아 회수하지 못하기 때문이다.

function test() {
  var element = document.getElementById('someElement');
  var id = element.id;
  
  // id를 사용하는 익명함수
  element.onClick = function() {
    alert(id);
  };
  
  element = null;
}

책 리뷰를 준비하고 진행하는 과정에서 학습했던 부분을 다시 한번(또는 그 이상) 학습하고, 어떤 방식으로 설명해야 할지 고민하다 보면 그냥 책으로 읽는 것보다 내용을 더 꼼꼼하게 이해할 수 있게 된다. 학습 방식을 고민하고 있다면 이 책 리뷰 방식을 적극 추천한다.

코딩 실습

책을 학습하고 나면 FE에서 빼놓을 수 없는 도구들(개발자 도구, webpack, test 관련 도구 등)을 학습하고 이후엔 실제로 코딩을 하는 실습(Drag&Drop, Todo list 구현 등)을 통해 익히기만 했던 내용들을 실제로 구현하게 된다. 책으로만 학습을 하다가 실제 구현 과제를 마주치게 되었을 때 막막함에 숨이 탁 막혔었다.

Drag&Drop을 구현할 때가 가장 어려웠었다. 문제 해결(problem solving) 위주의 코딩을 주로 하다 보니 DND를 구현할 때도 성능이 안 좋을 것 같아 다른 방식이 없을까 하는 생각에 막혀 돌아가느라 힘들었었다. 예를 들어 마우스를 움직일 때 매번 위치를 계산해 dropzone(drop 가능한 영역) 위에 있는지 판별하는 함수가 동작하는 게 맞나 싶어서 열심히 다른 방식을 생각했었는데 당연히 마우스를 움직일 때 매번 판별하는 것이 맞았다. 또 드래그 관련 이벤트 핸들러를 등록할 때 책에서 배운 데로 mousedown, mousemove, mouseup 핸들러를 한 번에 등록하는 방식으로 개발을 했었다. 근데 mousedown 핸들러 안에서 mousemove, mouseup 핸들러를 등록하고 mouseup 핸들러 안에서 다시 두 핸들러를 해제하는 더 효율적인 방법이 있는 걸 보고 책에서 배운 내용만 사용하는 것이 아닌 응용하기 위해 항상 고민해야겠다는 교훈을 얻었다.

// 기존
target.addEventListener('mousedown', onStartDrag);
target.addEventListener('mousemove', onDragging);
target.addEventListener('mouseup', onEndDrag);

// 변경
const onStartDrag = (target) => {
  ...
  target.addEventListener('mousemove', onDragging);
  target.addEventListener('mouseup', onEndDrag);
}
const onEndDrag = (target) => {
  ...
  target.removeEventListener('mousemove', onDragging);
  target.removeEventListener('mouseup', onEndDrag);
};

코드리뷰

실습에는 개발 후 코드리뷰를 받는 과정도 포함되어 있다. 베이스캠프 교육 때는 TF 팀원들의 코드리뷰 시에는 거의 확인만 하는 정도였는데, 실제 실무에서는 세세한 부분부터 코드의 전체적인 구조까지 코드리뷰를 받는 점이 색달랐다. 처음 받은 코드리뷰의 댓글은 100개가 넘었었다.. 보통 이 설정은 왜 사용했는지, 왜 이런 방식으로 구현했는지 등의 자세한 리뷰가 달리게 된다. 이런 리뷰를 통해 코드를 작성할 때부터 이 옵션은 이러이러해서 넣고 이 로직은 더 나은 방법이 없을까 고민하면서 짜게 되는 습관을 들이게 됐다. 이후에 다른 팀원의 코드리뷰에 참석하면서는 이런 방식으로도 작성할 수 있구나 하며 시야를 넓힐 수 있어 코드 작성 시 도움이 됐다.

프런트엔드가 처음이라면 처음엔 막막할 수 있다. 이때 가장 도움이 되는 건 자바스크립트에 친숙해지는 것이라고 생각한다. 이를 위한 방법 중 프런트엔드 관련 소식들을 메일링 받기를 추천한다. Medium daily digest 같은 경우 매일 메일을 받을 수 있는데 늦어도 2~3일에 한 번은 읽는 것을 목표로 하고 있다.

브랜드사이트

신입 기술 교육을 마친 후 가장 처음 진행했던 프로젝트는 브랜드사이트 개편이다. 먼저 브랜드사이트에 대해 설명하면 우리의 제품군을 소개하고 프런트엔드 개발에 도움이 될만한 FE 가이드위클리픽을 게시하는 사이트다.

ui.toast

ui.toast.com

협업

프로젝트를 진행하면서 디자이너 분과 처음으로 협업을 하게 됐다. 일반적으로 디자인을 요청하면 디자이너 분이 3개 정도의 디자인 시안을 보내주신다. 이후 랩 회의를 통해 결정을 한 뒤 전달하면 Zeplin이라는 UI 가이드 프로그램으로 자세한 디자인을 주신다. 개발자가 아닌 사람과 하는 협업은 처음이라 조금 서툴렀던 것 같다. 의견을 전달할 때 이 내용에 대해 알 거라고 생각하고 설명을 했지만 상대방이 이 항상 내가 전하려는 내용에 대해 아는 것은 아니었다. 기존에는 개발자하고만 협업을 진행했었기 때문에 나도 모르게 놓치고 있던 부분이었다. 앞으로도 해당 업무에 대해 잘 모르는 팀원과 협업할 일이 많을 테니 사소하지만 중요한 점을 일찍 배울 수 있어 다행이었다.

반응형 테스트의 중요성

테스트의 중요성 또한 배울 수 있었다. 컴포넌트의 UI 테스트의 경우 Storybook을 통해 이루어졌다. 반응형 UI 테스트를 위해 viewport 설정을 통해 데스크톱, 태블릿 등의 기기에 대한 테스트를 진행했다. toast storybook 하지만 현실은 달랐다. 미리 정해놓은 크기의 viewport에 대해서만 테스트하다 보니 그 사이의 크기에 대한 테스트가 진행되지 않았던 것이다. 꼼꼼히 테스트를 진행했더라면 일어나지 않았을 테고 늦어도 배포 전에 발견할 수 있었을 텐데 그러지 못했다. 또 배포 후에 모바일 기기에 대한 반응형이 제대로 동작하지 않던 이슈도 있었다. 분명 반응형으로 동작해서 모바일 기기의 화면에 딱 맞게 화면이 렌더링 되어야 하는데 더 크게 렌더링 되어 스크롤이 생겼다.

일단 viewport 메타태그의 initial-scale 값을 설정하여 임시로 해결을 했다.

// (스크린 크기/720) 값으로 배율을 설정
viewport.setAttribute(
  'content',
  `width=device-width, initial-scale=${screenWidth / 720}, shrink-to-fit=no` 
);

열심히 원인을 분석한 결과 최상단 레이아웃에 적용했던 한 줄의 스타일이 문제였다.

const Wrapper = styled.div`
  min-width: 720px;
  ...
`;

최소 너비를 720px로 고정시켰기 때문에 화면이 작은 모바일 기기에서는 항상 스크롤이 생기는 것이었다. 이 이슈 또한 배포 전 모바일 기기에서도 테스트를 진행했다면 미리 발견했을 문제다. 개발한 부분이 제대로 동작하는지 손으로만 테스트했어도 발견했을 것들을 미리 잡아내지 못했다. 항상 꼼꼼히 자신이 개발한 부분을 테스트해야 된다는 기초적인 부분을 다시금 깨달을 수 있었다.

dynamic import 문제 해결

기억에 남는 개발 이슈로는 컴포넌트의 dynamic import 시 발생했던 이슈를 꼽고 싶다. 브랜드사이트의 어플리케이션 페이지에서는 해당 어플리케이션의 데모를 지원하는데 이 데모 컴포넌트를 dynamic import 할 때 차트를 제외한 데모들이 전부 에러가 발생했었다.

원인은 ReactsetState() hook의 사용 방법에 있었다.

setApplication(m['default']); // m['default']는 dynamic import한 데모

dynamic import한 데모를 setState()로 업데이트하는데 이 데모 컴포넌트가 차트는 객체, 나머지는 함수의 형태를 띠고 있어 발생하는 문제였다. React의 공식 문서에서 원인을 알 수 있었다. setState()가 인자로 함수를 받게 되면 이전의 state를 이용해 갱신된 state를 계산하는 함수로 인식을 하고 처리하기 때문이었다.

setApplication(() => m['default']); // m['default']는 dynamic import한 데모

dynamic import한 데모를 반환하는 화살표 함수로 수정하여 이슈를 해결할 수 있었다. 공식 문서를 꼼꼼히 읽고 사용하는 습관을 길러 API를 올바르게 사용해야 한다는 교훈을 얻었다.

이미지 에디터

ImageEditor

https://github.com/nhn/tui.image-editor

브랜드사이트 개편 후 이미지 에디터의 메인테이너를 맡아 현재까지 이슈 관리, 업데이트 및 배포를 진행하고 있다. 이미지 에디터는 TOAST UI 제품군 중 하나로, fabricjs라는 canvas 라이브러리를 활용하여 개발된다.

mixin

이미지 에디터를 맡아 가장 기억에 남는 프로그래밍 기법으로 mixin을 꼽고 싶다. mixin 기법은 필요한 부분을 독립된 객체로부터 얻어와 탑재하는 재사용 기법이다. 다른 객체의 프로퍼티를 복사해 사용하는 방식이라고 생각하면 될 것 같다. 구현에 다양한 방법이 있지만 이미지 에디터에서는 주로 프로토타입을 이용해 구현한다. 기능의 반복을 감소시키고 재사용을 해서 굉장히 효율적이라고 생각하는 패턴이지만 사용하면서 느낀 단점으로는 프로토타입에 이 기능 저 기능을 끼워 넣는 기법이다 보니 무분별하게 사용하면 객체의 프로토타입을 오염시키기 때문에 조심히 사용해야 한다고 느꼈다.

다른 제품들에서도 사용하는 기법이기도 한데 tui.code-snippetCustomEvents를 구현해놓고 제품에 mixin해서 매번 새로 구현하지 않고 미리 구현해둔 커스텀 이벤트를 사용한다. 이미지 에디터를 사용하는 사용자 측에서 이벤트를 구독, 발생시킬 수 있게 하기 위해 사용을 한다.

import { CustomEvents } from 'tui-code-snippet';
...
CustomEvents.mixin(ImageEditor); // 커스텀 이벤트 mixin

// 사용 시
ImageEditor.on('특정 이벤트', (props) => console.log(props));
ImageEditor.off('특정 이벤트');
ImageEditor.fire('특정 이벤트', { props: '이벤트' });

이 커스텀 이벤트의 mixin 방식 말고도 이미지 에디터에서는 최상단 ImageEditor 단에서 ui action을 담당하는 action을 mixin해서 action에서 this를 공유해서 사용한다. 이때는 action에서 ImageEditor에 구현되어 있는 API를 이용하여 ui 구성 시 사용하기 위해서 mixin을 사용한다. 이 action은 ImageEditor의 하위 컴포넌트인 ui에서 사용하게 된다.

// imageEditor.js
class ImageEditor {
  ...
  imageEditorFunction() { // ImageEditor에 등록된 함수
    console.log('imageEditor');
  }
  ...
}

action.mixin(ImageEditor);

// action.js
export action = {
  someAction: {
    this.imageEditorFunction(); // ImageEditor의 함수 실행
  },
  ...
  mixin(ImageEditor) {
    extend(ImageEditor.prototype, this);
  },
};

마무리

이상으로 프런트엔드 개발자로서의 첫 발을 내디딘 후 약 1년여 동안 진행한 프로젝트와 이를 통해 배운 점 그리고 느낀 점을 적어보았다. 아직도 공부할 부분이 산더미만큼 남아있기도 하고 가야 할 길이 막막하지만 다음 1년은 이번 1년보다 노력해서 더 좋은 프런트엔드 개발자가 되는 것이 목표다. 또 지금 캘린더의 메이저 업데이트를 위한 프로젝트를 진행하고 있는데, 위 프로젝트들을 경험하며 얻었던 교훈들을 통해 멋지게 완성할 테니 기대해 주길 바란다.

임재언2021.05.11
Back to list