라이너 홈의 모바일에 대응하는 자세

안녕하세요, LINER에서 Web Frontend를 담당하고 있는 제니🍑입니다.
이번 알파 스프린트는 유난히 힘들었는데요 LINER Web Home의 모바일 대응을 위해 이
것저것 코드를 많이 뒤집어 엎었던(?) 과정을 이번에 공유해 드릴까 합니다.

LINER Web Home 이제 desktop에서 뿐만 아니라 Tablet, Mobile의 창 크기에 맞는 유연한 디자인으로,
그 어떤 기기에서도 라이너가 추천해주는 맞춤형 피드들을 보고, 개인의 하이라이트를 관리할 수 있게 된다는 것이
개인적으로도 무척 기대가 됩니다!

What? (문제 상황)

아키텍쳐 자체의 중요성


규모가 어느정도 있는 프로젝트의 개발을 하다 보면, 역시 가장 중요한 것은 Architecture라는 생각이 듭니다. LINER에 입사하기 전, 제가 했던 개인 프로젝트나 타 회사에서 했던 개발 프로젝트들을 보면 정말 무지막지한 코드의 더러움에 지금 다시 보면 헛웃음이 납니다. 말도 안되는 commit message와 한 파일 당 1000줄이 넘어가는 코드의 중복…. 갑자기 다시 찾아본 저의 github repository를 보다가 끔찍했던 모습들이 적나라하게 있었습니다.

끔찍했던 지난 날의 pull reqeust 흔적들..아마도 3년 후에 지금 제가 라이너에서 개발한 코드들을 보면 같은 생각을 하고 있을 수도

설계가 후순위가 되면 시스템을 개발하는 비용이 더 많이 들고, 일부 또는 전체 시스템에 변경을 가하는 일이 현실적으로 불가능해진다.
– Clean Archictecture, 21P

지금 회사에서 공부하고 있는 책의 서두에 나오는 말입니다. 돌아가는 행위(외부 품질)만 중시하고,설계(내부 품질)를 등한시하면 유지 보수 비용이 증가할 거라는 믿음은 기술 부채라는 메타포를 만들었습니다.

어쨋든 역시 개발을 할 때 가장 중요한 시작점이 되는 것은 설계 인데, 그것을 어떻게 하면 좋을지, 정해진 답이 있는 것이 아니라서 정말 난감했습니다. 특히 저 같은 주니어는 실무 개발 경험이 많지 않아서 “내가 생각하는 방식이 맞을까?” 하는 의심을 끊임 없이 하게 되었습니다.

모바일 대응에서의 아키텍쳐의 중요성


모바일 대응을 시작하면서, 모바일/데스크탑에 따라서 화면에 보여지는 component들이 다 다르고, 창의 크기마다 또 바뀌는 화면 구성에 의해서 코드의 양이 방대해지기 시작했고, 더 이상 현재의 폴더 구조가 ‘납득 가능한 구조’ 가 아니라는 판단이 들었습니다.

React 폴더를 사실상 꿰 뚫고 있는 저 스스로도 버그를 수정하거나, 어떤 코드를 찾을 때 어느 폴더에서 찾아야 할 지 모르겠는 상황이 도래해 버리고 말았습니다. 이렇게 되면 나중에 유지 보수를 다른 사람이 하게 되었을 때 “와 어떤 XX가 이따구로 짰냐!!” 라고 하고 저를 욕하는 그런 무서운 상상을 계속 하게 되었습니다.

결론적으로는, Mobile, Tablet Landscape (가로), Tablet Portrait (세로), Desktop 총 4가지의 경우의 수를 생각해서 모바일 대응을 해야 했던 저는 이번 기회에 내 입맛대로 구조를 갈아 엎어 보자는 생각으로 구조 설계를 시작하게 되었습니다.

리액트 프로젝트 진단하기


이건 무조건 뜯어고쳐야겠다 라고 생각했던 부분은 총 3 가지로 나눌 수 있겠습니다.

  1. 가독성 최악, 방대한 App.js
  2. Container와 Component 폴더의 불분명한 분리 기준
  3. Redux 상태 관리의 상태.

문제아 App.js

우선, React 에서 App.js은 npm start를 하고 가장 먼저 실행이 되는 부모 component으로서, 아래와 같이 기본적인 Routing을 관리하거나, route 마다 어떤 Screen을 보여줄 지 관리하는 전체 프로젝트의 뿌리 역할을 합니다.

import React, { Component } from "react"
import { BrowserRouter, Route, Link } from "react-router-dom"
import Home from "./Home"
import About from "./About"
import Topics from "./Topics"

class App extends Component {
  render() {
    return (
      <BrowserRouter>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
      </BrowserRouter>
    )
  }
}

우리 liner-react-project 같은 경우에는 App.js 의 코드가 357 줄 이었고, Routing 관리 뿐만 아니라, div 마다 어떤 component를 띄울지, 로그인/비로그인 의 경우의 수 관리, 등등 너무 많은 일을 하고 있었습니다.

<div> 마다 <Switch> 태그를 넣고 있어서 가독성 또한 매우 좋지 않았습니다. 언젠가는 이것을 기필코 뜯어 고치고 말겠다는 생각을 했는데, 드디어 그 기회가 모바일 대응을 하면서 생겼습니다.

Container vs Component

기존 프로젝트 구조는 Presentation Component인 ‘component’ 폴더와,

Container Component인 ‘container’ 폴더로 구분이 되어있었습니다.

  • Presentation component 와 Container component란?

프레젠테이션 컴포넌트 (Presentational Component)

● 어떻게 보여지는 지를 책임진다.
● 내부에 Presentational Component 와 Container 컴포넌트 모두를 가질 수 있고, 대게 DOM 마크업 과 자체 스타일을 가진다.
● this.props.children 을 통해 다른 컴포넌트 를 포함 하게끔 만들 수 있다.
● 어플리케이션의 나머지 부분에 대해 아무런 의존성을 가지지 않는다. (예를 들면 Flux 의 Action 이나 Stroe) 즉, 단독적인 Component 가 된다.
● 데이터를 불러오거나 변경하는 작업은 Presentational Component 에서 작성하지 않는다

컨테이너 컴포넌트 (Container Component)

● 어떻게 동작해야 할지를 책임진다.
● 내부에 Presentational Component 와 Container 컴포넌트 모두를 가질 수 있지만, 대게 전체를 감싸는 div를 제외하고 자체적인 DOM 마크업이나 스타일을 갖고 있지 않다.
● 데이터 및 데이터와 관련된 동작을 다른 Presentational Component 와 Container Component 에게 제공한다.

출처: https://blog.naver.com/backsajang420/221368885149

그런데 기능이 점점 추가가 되고 프로젝트의 규모가 커지면서 Presentation component들에서도 api 호출을 하고 어플리케이션의 나머지부분과 굉장한 의존성을 가지고 있는 것이 보였습니다.

또한, 모바일 대응을 시작하게 되면 이 presentation component와 container component 의 구분이 더더욱 의미가 없어질 것 같아서 이것이 문제가 있다는 생각을 하게 되었습니다.

상태 관리의 상태

현재 LINER React 프로젝트는 상태관리를 Redux로 쓰고 있는데 기타 미들웨어는 사용하고 있지 않습니다. (Saga나 Thunks와 같은) 그러다 보니, component들마다 모두 state를 갖고 있고 전역적으로 관리할 필요성이 분명히 있는

비동기 호출 후 변화되는 상태들을 모두 component 내부에서 하고 있습니다. 사실 이 부분은, 모바일 대응을 하면서 손을 대기에는 양이 너무 많고 품이 많이 드는 작업이라서 분명히 필요한 작업이지만 후순위에 미뤄둔 것 같습니다.

How? (문제 해결)

문제 해결이라고 하기에는 거창하지만, 나름대로 많은 블로그, 오픈소스 리액트 프로젝트들을 찾아보면서 결국에는 제가 생각했을 때 가장 직관적인 프로젝트 구조로 재설계를 해야겠다는 생각을 했습니다. Presentation, container component 구조도 좋지만 LINER Web Home같은 경우에는 url 로 폴더 구조를 나누는것이 더 좋겠다는 생각이 들었습니다.

그래서 다음과 같이 폴더를 다시 정리하는 작업을 했습니다. container/ components가 아닌 pages/components로 기준을 분리하고, pages는 getliner.com 의 url 마다 폴더를 생성해서 url마다 component와 screen을 관리할 수 있게 되었습니다.

따라서 다음과 같이 getliner.com/(pathname) 의 pathname 별로 폴더를 생성했고, /home과 /myhighlights같은 경우에는 그 안에서도 갖고 있는 pathname 이 총 4가지가 있어 이 경우에는 아래 폴더의 index.js에서 그 route들마다 맞는 화면 (screen)을 뿌려주게 끔 routing을 관리하게 되었습니다.

또한, 이렇게 url마다 사용되는 components를 따로 관리하게 되니, 더 쉽게 component을 찾을 수 있게 되었고, 무엇보다도, 모바일을 대응할 때 각각 필요한 component을 폴더별로 예쁘게 관리할 수 있게 된 장점이 있습니다. src 안에 있는 가장 밖의 component는 전역적으로 필요한 구성품인 button, modal, dropdown등 만을 관리하도록 넣어놨습니다. 제가 생각했을 때는 이것이 조금더 직관적인 구성이지 않을까 합니다.

App.js를 깔끔하게 정리할 수 있는 방법으로는 App.js에서 div마다 component들을 넣어주는 것이 아닌, 기기 종류/화면크기 마다 화면 template을 만들어서 필요한 component을 끼워넣어줄 수 있는 Wrapper Component를 생성해서 코드를 깔끔하게 정리할 수 있었습니다.

이렇게 Wrapper components를 사용하면 다음과 같이 깔끔하게 반응형 관리를 할 수 있게 됩니다.

더 좋은 방법이 있을 가능성이 더 클 것 같지만, 적어도 현재 상태에서는 조금 더 reasonable한 구조가 되었다는 것에 조금은 뿌듯해졌습니다.

So What?

아직 저는 모바일 대응을 고군분투하고 있습니다. 그리고 아직 갈 길이 멀었습니다. 또, 엄청난 코드 중복과 씨름하고 있습니다. 구조는 많이 바꿨지만 , css 코드의 중복에 대응하는 것은 정말 힘들더군요.

css 코드의 양을 줄이는 것도 정말 중요한 작업인데, 아마도 다음 알파 스프린트에는 제가 그 부분을 미래의 제가 어떤 방법으로 찾아 내지 않을까? 하는 막연한 기대감 속에서 이번 글 마칩니다. 모바일 이렇게 힘들게 개발했으니 출시가 되면 많이 써 주셨으면 좋겠습니다.

라이너는 채용 중

#we’re_hiring

라이너 팀에서는 의미있는 관계 속에서 빠르게 성장하며 의미있는 일을 함께 하실 똑똑한 분들의 합류를 기다립니다!

자세한 내용은 아래 채용 사이트 링크를 통해 확인하실 수 있습니다.