딜리셔스 인턴 생활기

인턴1의 개발자 비슷한 사람으로 거듭나기

유지민
2021.10.12

안녕하세요. 얼마 전 2달 간의 인턴 생활을 마친 딜리셔스 백엔드 개발자 유지민입니다.

한참 헤매던 인턴 첫 주와 비교하면 그래도 지금은 개발자의 업무라는 것이 어떻게 돌아가는지 조금 감이 잡힌 상태라는 생각이 드는데요, 지난 인턴 생활을 회고하며 기록으로 남겨보도록 하겠습니다.

입사 2주차

저희 팀에서는 루비온 레일즈를 주요 프레임워크로 사용하고 있습니다. 저를 포함한 저희 팀 인턴 동기들 모두 이에 대한 경험이 없었기 때문에, 첫 2-3주간은 회사에 적응하고 루비와 ROR에 대해 교육을 받고 공부하는 시간을 가졌습니다.

바쁘신 와중에도 시니어 개발자 분들이 교육을 직접 해 주었습니다. 루비만의 언어적인 특징에서부터 RSpec, 리액트 연동까지 아우르는 압축적이지만 양질의 교육이어서, 이후 프로젝트를 진행하면서 전체적인 프레임워크 구조를 파악할 때나 중간에 막히는 부분이 생겼을 때 ‘그때 들었던 그 개념을 더 찾아보면 되겠다’ 하는 식의 가이드라인으로써 아주 큰 도움이 되었습니다.

이외에도 동기들끼리 각자 공부한 부분을 정리해서 공유하기도 하고, 회사에서 진행한 뉴렐릭 온사이트 교육에 참가해 보는 등 다양한 경험을 하였습니다.

입사 6주차

이때 쯤에는 맡겨진 첫 프로젝트 개발을 한창 진행하고 있었습니다.

저희 팀은 인턴에게도 토이 프로젝트가 아닌 실무 프로젝트를 바로 경험해 볼 수 있는 기회를 주고 있습니다. 이때 수행한 것이 어드민 권한관리 프로젝트로, 회사 내부 관리자 시스템에서 해당 직원이 속한 그룹에게 주어진 메뉴 권한에만 접근 가능하도록 하는 것이었습니다. 이를 위해 권한 관련 페이지들을 새로 만들고 기존의 파편화된 코드를 정리했습니다. 로그인한 유저가 권한을 가진 메뉴들만 네비게이션 바에 동적으로 렌더링 하고, 권한이 없는 메뉴로의 접근을 막는 로직을 짜는 작업이었습니다.

위와 같이 모델링 된 테이블을 가지고 작업을 진행했습니다.

우선 이러한 메뉴에 대한 조회, 생성, 수정(페이지1), 그룹별로 가진 메뉴 권한(페이지2), 계정 별로 속한 그룹이나 예외권한(페이지3)을 관리할 수 있는 기능을 가진 페이지들을 만들면서 레일즈 구조나 레일즈의 빌트인 ORM인 ActiveRecord로 쿼리를 작성하는데 익숙해질 수 있었습니다.

그리고, 현재 로그인한 유저가 권한을 가진 메뉴나 URL에 대해 계산해서 컨트롤러나 뷰에서 사용할 수 있도록 인스턴스 변수에 담아주는 모듈을 작성한뒤 authentication gem인 devise에서 제공하는 SessionsController나 모든 메뉴 컨트롤러들이 상속받고 있는 AdminController에 include해서 로그인 후 랜딩할 URL을 결정한다든가 앞서 말한 렌더링, URL 확인 같은 로직을 수행할 수 있게 했습니다.

그런데 한 가지 고민 거리가 있었습니다. 사실상 유저가 가진 메뉴, URL에 대한 데이터는 로그인 이후 한 번만 계산하고 세션에 저장해 두면 되는 정보입니다. 하지만 막상 세션에 저장하려니 전체 어드민 프로젝트 상에서 session store가 CookieStore로 설정되어 있어서 4KB를 넉넉하게 넘어버리는 cookie overflow 가 발생했습니다. 세션에 저장하기도 어렵고, 그렇다고 프로젝트 전체에 영향을 줄 수도 있는 session store 타입을 바꾸기도 애매한 상황이었습니다.

이때, 대신 Redis에 캐싱 해보면 어떻겠냐는 조언을 받고 고민을 해결할 수 있었습니다. 주위에 친절한 동료 개발자가 있다는 것이 얼마나 좋은지 실감하는 순간이었습니다. 혹여나 제가 어드민에 몹쓸 짓을 한 것은 아닌지 걱정되는 마음도 있지만, 어쨌든 정말 많이 배울 수 있었던 프로젝트였습니다.

입사 11주차

어느새 시간이 흘러 무사히 어드민 배포를 마치고 인턴 기간도 끝났습니다. 너무 좋은 동대문 휴가도 다녀오고 나서 작업한 권한관리 프로젝트에 대해서 팀원들 앞에서 발표도 했습니다.

사실 프로젝트를 진행하면서 난관으로 예상했던 부분이 있었습니다.

이런 식으로 메뉴 레코드 간에 부모 자식 관계를 가지고 있어서 트리 구조로 메뉴 테이블을 순회하고 데이터를 읽어들여야 했는데, 다행히도 ancestry라는 gem을 소개받아서 이 부분을 간단하게 해결할 수 있었습니다.

프로젝트 중에는 트리 관련 쿼리는 ancestry가 알아서 잘 처리해 줬겠거니 하고 지나가버려서 발표 때 메뉴 테이블 관련 질문이 들어왔을 때도 잘 답변하지 못했는데, 프로젝트 마무리 후 여유 기간에 공부해보니 RDB에서 계층 구조를 표현하는 모델들도 몇가지나 존재하고 트리 관련 gem들도 모두 이 모델에 기반해 만들어 졌다는 것을 알 수 있었습니다.

그래서 각각의 gem을 사용해서 개인적으로 생성해본 약식의 메뉴 테이블들을 공유해보고자 합니다.

Adjacency Lists

acts_as_tree gem이 이 모델을 기반으로 작성되었습니다. parent_id 필드를 통해 부모 레코드를 참조하는 식으로 Tree 구조를 표현하는 1:n self referential 관계 모델입니다. 사실 간단하기 때문에 굳이 gem을 이용하지 않아도 괜찮지만 acts_as_tree를 통하면 parent, child, root?, leaf?, siblings, ancestors 같은 메소드들을 사용할 수 있기에 편리할 것입니다.. 다만 depth가 깊어질수록 join 해야 할 횟수가 늘어나기 때문에 복잡한 트리구조를 구현하는 데는 다른 선택지를 고려하는 것이 좋을 듯합니다.

Nested Set

nested set 모델에서는 left, right 두가지 필드를 가지고 계층 구조를 표현하는데요, 트리를 순회하면서 각 노드는 모든 자손들의 숫자가 자신의 left, right 값 사이에 속하도록 넘버링 합니다.

실제로 nested set을 기반으로 작성된 awesome_nested_set을 통해서 메뉴 테이블을 seeding 해보니 위와 같이 lft, rgt 필드를 가진 테이블이 생성되었습니다.

BETWEEN ancestor.lft AND ancestor.rgt

같은 식으로 쿼리를 작성하면 돼서 adjacency lists 방식보다 읽는 것은 훨씬 효율적이지만, 위 테이블에서 새 메뉴를 추가하면 그 모든 오른쪽 메뉴들의 lft, rgt 값이 2씩 증가된 것에서 볼 수 있듯이 잦은 업데이트에는 취약한 모델입니다.

Path Enumeration

이번 프로젝트에서 사용한 ancestry gem이 바로 이 path enumeration(혹은 materialized path)모델의 구현체였던 것이었습니다! ancestry 필드에 조상 id들을 순서대로 모두 열거하고 아래와 같은 패턴 매칭으로 쿼리를 작성합니다.

WHERE childpath LIKE parentpath || %

Closure table

closure_tree라는 gem이 이 모델을 따릅니다. G=(V,E)로 표현되는 그래프처럼 노드에 해당하는 menus 테이블과 자기 자신을 포함한 모든 자손들에 대한 direct edge를 저장하는 menus_hierarchies라는 두가지 테이블이 생성됩니다. 이 gem의 github 페이지에서 읽기는 1 SELECT 문, 업데이트 작업도 2-3 INSERT/UPDATE 문이면 된다고 소개하고 있듯이 읽기와 쓰기 작업이 모두 효율적인 모델이라고 합니다.

마치며

인턴기간과 프로젝트를 통해 정말 많은 것을 새로 배울 수 있었는데요, 입사 전 협업 경험이 거의 전무하다 싶은 저로서는 실제 실무 개발 사이클을 한 번 돌아볼 수 있었다는 것이 무엇보다 값진 경험이 되었습니다.

PR을 올려서 코드 리뷰를 받고 개발해야 하는 기능에 관해서 기획자와 소통하고 QA 과정을 거쳐 실제 서버에 배포가 되고 제가 짠 코드로 변경된 사항들을 실제로 다른 분들이 사용하게 되는 이 전체 과정들을 돌아보면서 막연했던 개발자 업무에 대해 빠르게 파악하고 적응할 수 있었습니다.

그 밖에도 전반적으로 모두 따뜻한 회사분들이 이 숫기없는 인턴이를 잘 맞아주시고, 주기적인 티타임과 만두타임으로 긴장도 풀어주셔서 더욱 수월하게 회사에 적응할 수 있었습니다.

앞으로도 인턴 시절의 설레임을 잊지말고 계속 정진하는 개발자가 되도록 하겠습니다.

유지민

딜리셔스 백엔드 개발자

"개발자 라이프 화이팅, kt 위즈도 화이팅!"