Skip to content

Instantly share code, notes, and snippets.

@eunjae-lee

eunjae-lee/README.md Secret

Last active Apr 24, 2020
Embed
What would you like to do?
Web Development Stack @ 2018

Web Development Stack @ 2018

2018년 4월 현재 기준으로 어떤 기술들을 활용하여 웹사이트를 개발해야 할지에 대해 정리했습니다. 분량은 방대하지만 Proposal 이기 때문에, 꼼꼼히 봐주시고 궁금하신 점이나 의견 있으시면 말씀해주세요.

Backend + Frontend

전통적인 Rails Application 에서는 controller 와 view 코드를 작성하면서 Backend 와 Frontend 간의 strong coupling 이 생겼는데요. 새로운 프로젝트에서 Rails 는 API 서버로만 사용됩니다. 그리고 Frontend 를 위해서는 별도의 프로젝트(repo)가 존재하게 됩니다.

최신의 Angular, React, VueJS 등에서 가이드하는 Frontend 개발 방식은 다음과 같습니다.

  1. Angular, React, VueJS 등에서 제공하는 CLI 를 통해 NodeJS 기반의 Frontend 프로젝트를 생성합니다.
  2. 개발하는 동안에는 NodeJS 로컬 서버를 띄우고 개발합니다. 이 서버가 떠 있는 동안 Lint, Debugging 관련 기능들이 제공됩니다.
  3. 배포할 때는 제공되는 명령어를 통해 프로젝트 전체를 static html, js, css 로 빌드하고, S3 등 file storage 에 올립니다.
  4. 업로드된 static html, js, css 로 된 웹사이트는 API 서버와 통신하여 작동합니다.

Backend

Backend 로는 Ruby on Rails 를 사용합니다. 멤버들이 계속 써와서 익숙하며, 굳이 다른 Framework 로 교체할 니즈가 없습니다.

API Server

다만 Rails 를 API 서버 용도로만 사용하게 되는 만큼, grape 라는 gem 을 사용합니다. grape 를 사용하면, 보다 명시적으로 API 파라미터 스펙을 코드에 기술할 수 있습니다. Rails + grape 조합뿐 아니라 Sinatra + grape 조합도 가능하지만, Rails 가 제공하는 ActiveSupport, ActiveRecord 등의 gem 을 사용해야 할 거라 Rails + grape 조합으로 결정했습니다.

format :json

params do
  requires :first_name, type: String
  optional :last_name, type: String
end

post 'users/signup' do
  # user = ....
  return user
end

Rails 에서 제공하는 Rails API 라는 패키지가 있지만, 별다른 기능이 있는 게 아니라 API 서버를 위해 만들어진 rails 의 subset 수준입니다. 반면 grape 는 오랜 기간에 걸쳐 개발되어 왔으며, grape 를 대체할 만한 다른 gem 은 찾지 못했습니다.

살펴보기 : https://github.com/ruby-grape/grape

Role-Based Access Control

용어들과 용어 간의 Hierarchy 는 AWS 의 IAM 에서 따왔습니다.

  • 용어
    • User
    • Role
    • Policy (Effect, Action, Resource)
  • 설명
    • User 는 여러 Roles 을 가집니다.
    • 하나의 Role 은 여러 Policies 를 가집니다.
    • 하나의 Policy 는 Actions 가 Resources 에 대해 어떤 Effect 를 갖는지에 대해 기술합니다.
  • 예시
    • User ACampaignManager 라는 Role 을 가집니다.
    • CampaignManager 라는 Role 은 CRUD_ALL_CAMPAIGNS_UNDER_COMPANY 라는 하나의 Policy 를 가지는데,
    • 이 Policy 의 내용은, 해당 유저의 회사에 속하는 모든 Campaign 이라는 Resources 에 대해서 [:read, :update, :create, :delete] 라는 Actions 에 대해 Allow 라는 Effect 를 가진다는 것입니다.

AWS 의 IAM 에는 Policy 의 속성 중 하나로 Request Condition 이라는 개념도 있는데, 이는 해당 Policy 가 효과를 발휘하는 부가적인 조건을 명시합니다. 예를 들면 특정 날짜 범위에 속하는지, 유저가 MFA 인증을 했는지, 유저가 특정 IP 대역 내에 있는지 등입니다. 하지만 이는 너무 상세한 기능이라, 이번 프로젝트에 쓰일 가능성이 낮다고 보고, 제외한 채 진행합니다.

이를 구현하기 위한 gem 이 다양하게 있는데, 지난 프로젝트에서는 Role 을 위해서는 rolify 를, Policy 를 위해서는 cancancan 을 사용했습니다. 이번 프로젝트에도 동일하게 진행합니다. 하지만 유의할 점은 다음과 같습니다.

유의사항1 위 모델에서 Role 은 Policy 만 갖고, Resource 를 갖진 않습니다. rolify gem 에서는 role has_many resources관계를 제공하고 있지만, 우리 프로젝트에서는 그 부분을 사용하지 않습니다.

유의사항2 API Server 가 Client 에게 Resource(s) 를 내려줄 때, 해당 유저가 각각의 Resource 에 대해 CRUD 권한이 있는지, 그리고 그 Resource 의 클래스에 대한 CRUD 권한이 있는지 정보를 같이 내려주도록 합니다.

{
  data: {
    campaigns: [{...}, {...}, ...]
  },
  permissions: {
    class: ['create'],
    instance: {
      read: [1, 3, 4, 5],
      update: [1, 3, 5],
      delete: []
    }
  }
}

data 와 별개로 Permission 정보를 알아야 그에 맞게 view 를 구성할 수 있습니다. 위는 캠페인 목록을 보여주는 화면에서 호출하는 /campaigns API 의 응답 예시입니다. 위 예제에 따르면 Campaign 클래스에 대해 create 권한을 갖고 있으니, 유저에게 캠페인 생성 버튼을 보여줄 것입니다. 그리고 id=4 인 Campaign 에 대해서는 수정 버튼을 보여주지 않아야 합니다.

이와 같이 Resource 와 Permission 둘 중에 하나라도 클라이언트가 갖지 못하면 view 를 그릴 수 없기 때문에 둘을 한 API 의 응답으로 묶었습니다. 물론 이런 Permission 체크가 필요 없는 view 도 있을 수 있기 때문에 permissions 부분은 API 의 파라미터를 통해 선택적으로 가져올 수 있게 합니다.

유의사항3 기존 프로젝트에서 ability.rb 파일을 비효율적으로 구현했던 부분을 개선하도록 합니다.

class AbilityTest1
  include CanCan::Ability

  def initialize(user)
    can :read, App, id: user.company.apps.collect { |app| app.id }, status_id: 2
  end
end

기존에는 위와 같이 read 할 수 있는 App 의 id 를 모두 모아 지정해주는 방식이었습니다. 그래서 Ability 객체가 하나 생성되는 순간 쿼리가 실행되어 id 목록을 뽑게 됩니다.

하지만 cancancan 의 문서를 잘 보면, 실제 id 의 배열을 넣는 방법보다는 hash conditions 를 넣을 것을 권장합니다. 여기서 hash conditions 는 ActiveRecord 의 where 절에 들어가게 됩니다.

class AbilityTest2
  include CanCan::Ability

  def initialize(user)
    can :read, App, company: user.company, status_id: 2
  end
end

위의 company: user.company, status_id: 2 가 hash conditions 입니다. AbilityTest2 의 경우 ability 객체가 생성되는 것만으로는 쿼리가 실행되지 않습니다.

아래는 테스트 코드입니다. RAILS_ENV=production rails console을 띄우고 확인해볼 수 있습니다. 실제로 실행되는 쿼리를 로깅해보려면, config/environments/production.rbconfig.log_level 값을 :debug 로 바꾸면 됩니다(다만, 일부 쿼리는 캐싱되어 실제 데이터베이스 질의 없이 응답될 수도 있습니다).

user = User.find 61
not_my_company = Company.find 136
not_my_app = not_my_company.apps.first
my_app = user.company.apps.first

Not Good

>> AbilityTest1.new(user).can? :read, not_my_app
  Company Load (6.2ms)  SELECT  "companies".* FROM "companies"  WHERE "companies"."id" = $1 LIMIT 1  [["id", 3]]
  App Load (20.0ms)  SELECT "apps".* FROM "apps"  WHERE "apps"."company_id" = $1  [["company_id", 3]]
false

>> AbilityTest1.new(user).can? :read, my_app
  App Load (48.7ms)  SELECT "apps".* FROM "apps"  WHERE "apps"."company_id" = $1  [["company_id", 3]]
true

>> App.accessible_by(AbilityTest1.new(user))
  App Load (30.1ms)  SELECT "apps".* FROM "apps"  WHERE "apps"."company_id" = $1  [["company_id", 3]]
  App Load (18.9ms)  SELECT "apps".* FROM "apps"  WHERE "apps"."id" IN (159, 161, 173, 556, 67, 68, 118, 91, 77, 116, 147, 66, 256, 257, 149, 148, 82, 776, 100, 144, 92, 555, 111, 70, 99, 101, 495, 104) AND "apps"."status_id" = 2
#<ActiveRecord::Relation [...

Good

>> AbilityTest2.new(user).can? :read, not_my_app
  Company Load (5.8ms)  SELECT  "companies".* FROM "companies"  WHERE "companies"."id" = $1 LIMIT 1  [["id", 3]]
false

>> AbilityTest2.new(user).can? :read, my_app
true

>> App.accessible_by(AbilityTest2.new(user))
  App Load (42.6ms)  SELECT "apps".* FROM "apps"  WHERE "apps"."company_id" = 3 AND "apps"."status_id" = 2
#<ActiveRecord::Relation [...

유의사항4 기존 프로젝트에서 ability.rb 파일에 길게 내용을 적었는데, 이번에는 위에 정의한 모델에 입각해서 Role, Policy, Action, Resource, Effect 를 기술하도록 강제하고, 그게 Ability 클래스로 포함되도록 하는 helper 클래스를 작성할 예정입니다.

살펴보기

Modular Monolith

비즈니스 로직에 연관되지 않는 유틸 등에 대해서는 가급적 별개의 gem 으로 분리하도록 합니다. 코드를 물리적으로 분리함으로써 관리의 용이성, 테스트의 용이성 등을 얻을 수 있습니다. 하지만 repository 까지 나눈 후에, gem 을 rubygems.org 로 배포하고, 그걸 다시 dependency 로 넣는 작업은 꽤나 번거로운 데다가 그렇게 까지 해서 얻는 이득이 이번 프로젝트 진행함에 있어서 크지 않을 거라 예상합니다. 그래서 아래 글에 설명된 내용을 바탕으로 local gem 형태로 ./gems/gem_name 경로에 분리하도록 합니다.

아래 글에서는 비즈니스 로직에 관련된 코드는 ./engines 에, 관련되지 않은 코드는 ./gems 에 넣었다고 하는데, 그렇게 까지 분리하진 않고, 비즈니스 로직에 관련되지 않은 코드들을 ./gems 에 넣는 정도로 적용하도록 합니다.

살펴보기 https://medium.com/@dan_manges/the-modular-monolith-rails-architecture-fb1023826fc4

Authentication

유저 인증을 위해 기존 프로젝트에서는 devise gem 을 사용했습니다. 비슷한 대안으로는 Thoughtbot 에서 나온 clearance 라는 gem 도 있습니다. 하지만 두 gem 모두 view 를 갖는 Rails Application 에서 돌아간다는 전제하에 자체적인 view 관련 기능들이 붙어 있습니다. rails 를 API Server 로만 사용하려는 목적을 달성하기 위해서는 두 gem 을 많이 tweak 해야 하는 부담이 있어서, 이 목적에 부합하는 knock 라는 gem 을 사용하기로 합니다.

https://github.com/nsarno/knock

Knock is an authentication solution for Rails API-only application based on JSON Web Tokens.

그리고 쿠키 기반이 아닌 토큰 기반의 authentication 을 구현합니다.

그 외에 가입 환영 메일 등 devise 에서 제공하던 기능들에 대해서는 필요에 따라 별도의 gem 을 통해 구성하도록 합니다.

살펴보기

Testing Framework

Testing Framework 로는 rspec 을 사용합니다. 그리고 테스트 시에 필요한 더미 데이터는 factory_bot 을 통해 처리합니다. 기존 프로젝트에서도 부분적으로 rspecfactory_bot 의 구버전인 factory_girl 을 사용했습니다.

살펴보기

rbenv vs rvm

Ruby 버전 관리 시스템으로는 rvm 보다 rbenv 가 선호되는 추세이지만, 협업 시 누가 어떤 것을 사용해도 큰 관계는 없습니다.

Versions

ruby 는 2.5.0 , rails 는 5.2 를 사용합니다.

Ruby Style Guide

ruby-style-guide 를 보면 Ruby 로 개발 시 지양, 지향해야 할 패턴에 대해 예시를 들어 자세히 정리되었습니다. 스타일을 통일하는 목적도 있지만, 안좋은 결과를 초래하는 Anti-pattern 에 대해서도 잘 설명하고 있습니다.

이 모든 패턴을 개발자가 한 번에 다 외우고 익히기가 어렵기 때문에, Rubocop 이라는 gem 을 사용합니다. Rubocop 은 ruby-style-guide 내용을 기반한 코드 분석 툴입니다. 다양한 IDE 에 Rubocop 플러그인이 존재하기 때문에, lint 기능을 통해 코딩하면서 바로바로 잘못된 패턴에 대해 알 수 있습니다.

한편 Sandi Metz 라는 Ruby 개발자가 있는데, 한 팟캐스트에서 자신의 Ruby 코드를 작성할 때 지키는 룰에 대해 소개했습니다. 읽어보면 다소 과격할 정도로 strict 합니다. 하지만 그 룰을 지키려 노력하다 보면 작성하는 코드에 대해 다시 한번 생각할 기회를 갖게 되기 때문에 긍정적인 부분이 크다고 생각합니다. Sandi Metz 가 주장한 룰을 그대로 수용할 수도 있고, 완화시킨 버전을 사용하기로 멤버끼리 합의할 수도 있습니다. 그리고 이 모든 건 Rubocop 에 설정으로 포함시킬 수 있습니다.

살펴보기

Frontend - JavaScript

똑같은 UI 를 구성함에 수없이 많은 방법이 존재하기 때문에, 잘 정돈된 방법을 멤버가 서로 합의하여 그 룰을 지키는 것이 중요합니다. 그리고 그게 어렵지 않도록 도와주는 Framework/Library 를 선택하는 것도 중요합니다.

Angular vs React vs VueJS

Frontend 개발 Framework 으로 현재 가장 인기 있는 3가지가 Angular, React, VueJS 입니다. 간단히 말하면 새로 시작하는 프로젝트에서 Angular 는 쓰이지 않는 추세이고 React 와 VueJS 중에 선택의 문제입니다.

React 는 Facebook 에 의해 개발되고 있으며, 상당히 큰 규모의 eco-system 을 가지고 있습니다. Core 를 제외하고는 대부분 커뮤니티에 의해 React 관련 주변 library 들이 제작되고 있는데, 그러다 보니 비슷한 기능을 하는 library 들이 여럿 있고, 선택의 이슈가 자주 등장합니다. 이는 장점일 수도, 단점일 수도 있습니다. 한편 컴포넌트를 개발함에 있어 템플릿에 HTML 과 JavaScript 를 섞어 사용하는 형태여서, 자칫 가독성이 떨어질 여지가 있습니다(jsp, erb 등에서 로직과 HTML 을 섞어서 가독성이 떨어지던 문제).

VueJS 는 Angular 나 React 에 비해 등장한 지 오래되진 않았지만, 구글에서 일했고, MeteorJS 를 만들었던 Evan You 라는 사람의 주도하에 중앙집권적(?)인 형태로 개발되고 있어서 방향성이 뚜렷하다는 특징이 있습니다. React 에 비해서는 주변 library 들이 아직 많이 않다는 단점이 있습니다.

지금 시점에 새로운 프로젝트를 시작하는데 React vs VueJS 는 선택의 문제이고 어느 것이 우세하다고 보긴 어려운 것 같습니다. 여러 Article 을 읽고 개인적인 선호를 포함해 내린 결정은 VueJS 로 진행하는 것입니다.

아래 중 첫 번째 링크 정도만 읽어보셔도 됩니다

Single-Page-Application or Multi-Page-Application?

웹사이트를 개발하기에 앞서 SPA vs MPA 중에 선택을 해야 합니다. SPA 가 갖는 대표적인 두 가지 단점이 있습니다. 검색 엔진 최적화(SEO)가 어렵다는 것과, 속도가 느리다는 것입니다.

SEO 문제는 React 나 VueJS 에서 제공하는 기능을 통해 해결이 가능한 걸로 조사되었습니다. 하지만 애초에 이번에 만들려는 프로젝트에는 SEO 가 필요하지 않기 때문에 논외로 합니다.

속도의 이슈는, 그간 JavaScript Framework 이 발달함에 따라 크게 개선되었고, 특히나 컴포넌트 기반의 개발이 보편화되고, 컴포넌트도 lazy loading 됨에 의해 리소스 로딩이나, 아니면 성능 자체에 별다른 이슈는 없을 것 같습니다. 물론 필요에 의해 Multi-Page 이어야 하는 부분이 있다면, React 나 VueJS 에서 별다른 문제없이 지원됩니다.

그렇기 때문에 SPA 로 하되, 필요시 페이지를 추가한다는 가정을 깔고 시작합니다. 이를 위해 페이지를 컴포넌트 단위로 잘 쪼개고, 필요한 컴포넌트를 그때그때 로딩하는 방식을 취합니다. 그리고 이를 위해 모듈 로더인 webpack 을 사용할 예정이고, 이는 VueJS CLI 에서 생성해주는 프로젝트에 기본 탑재됩니다.

살펴보기

Component-Based Development (CBD)

W3C 에서는 WebComponent 스펙에 대해 논의하고 있습니다. React 나 VueJS 에서는 이 스펙과는 별도로 자체적으로 컴포넌트를 구현했습니다.

https://code.tutsplus.com/tutorials/stateful-vs-stateless-functional-components-in-react--cms-29541 위 Article 에서는 컴포넌트의 구분에 대해 개념적으로 잘 정리하였습니다.

  • Class Components vs. Function Components
  • Stateful Components vs. Stateless Components
  • Container Components vs. Presentational Components

컴포넌트를 개념적으로 어떻게 분리하고 조합해서 활용해야 할지 충분한 고민이 없게 되면, 결국 Page 에 거의 가까운 단위의 Component 혹은 UI 기능과 비즈니스 로직이 섞여버려 재활용 불가능해진 Component 가 생겨버리게 될 것입니다.

Single-File Component

VueJS 에서는 Single-File Component 기능을 제공합니다. 확장자가 .vue 인 단일 파일 내에 템플릿, 로직, 스타일을 한 번에 담을 수 있습니다.

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
module.exports = {
  data() {
    return {
      greeting: 'Hello'
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

Node Version Manager

Node 의 버전 관리를 위한 툴이 nvmn 이 있는데, n 은 활발히 maintain 되지 않고 있으며 buggy 하여, nvm 을 사용합니다.

살펴보기 https://github.com/creationix/nvm

Version

node 는 8.11.1 을 사용합니다.

ECMAScript 6 (ES6)

JavaScript 는 오랜 기간에 걸쳐 나아지고 있습니다. 기존에 가지고 있던 언어적인 문제점들이 많이 해결됐고, 필요로 했던 기능들이 생기고 있습니다. 또한 그 과정에서 새로운 문법도 등장하고 있습니다. ES6 에 대해 잘 모른 채 ES6 문법으로 적힌 코드를 보면 혼란스러울 수 있습니다. ES6 를 지원하지 않는 구 브라우저도 babel 이라는 library 를 통해 하위 호환을 이루어 냅니다.

살펴보기

JavaScript vs TypeScript

큰 규모의 프로젝트를 진행하다 보면, 지금 호출하려는 메소드에 어떤 타입의 파라미터를 넣어야 하는지 기억이 나지 않아 메소드의 구현을 들여다 보고서야 알게 되는 경우들이 많습니다. 특히 JavaScript 쪽에서는 의도치 않게 변수에 숫자 1을 담으려다 문자 '1' 이 담기는 일도 비일비재합니다. 그래서 이번 프로젝트는 Compile 시점에 Type-Checking 을 해주는 TypeScript 를 사용하여 개발합니다. TypeScript 가 가지고 있는 다양한 기능들은 미뤄두고, 기본적인 타입 선언 기능과, interface 를 통한 추상화 기능 정도를 ES6 위에 얹어 사용하는 정도라고 보면 됩니다.

살펴보기

JavaScript Style Guide

Airbnb 에서 JavaScript Style Guide 를 배포했습니다. ruby style guide 와 비슷하게 사례들을 모아놔서, 어떻게 JavaScript 를 사용하는 게 좋은지 알려줍니다. TypeScript 를 사용하기로 결정했지만, 여전히 이 가이드는 유효합니다.

살펴보기 https://github.com/airbnb/javascript

Testing Engine

CBD 의 장점 중 하나는, 개별 컴포넌트에 대한 테스트가 용이하다는 점입니다.

Unit Test 는 Facebook 에서 나온 Jest 를 사용합니다. Selenium 기반으로 동작하는 End-to-End (e2e) 테스트는 Nightwatch.js 를 사용합니다.

VueJS 는 vue-test-utils 라는 유틸을 통해 Jest 와 Nightwatch.js 에서 테스트 코드를 작성할 때, VueJS 의 컴포넌트들에 특화된 테스트를 가능하도록 합니다.

살펴보기

Linter

https://eslint.org/https://palantir.github.io/tslint/ 를 사용합니다. 이는 Vue CLI 가 생성해주는 프로젝트에 기본으로 탑재됩니다.

Pug

템플릿 개발을 위해 순수한 HTML 이 아닌, pug 라는 library 를 사용합니다. 이는 erb, jade, haml, slim 등 rails 에서 사용하던 템플릿 언어와 비슷한 것으로, NodeJS 진영에서 널리 사용됩니다.

<template>
<div class="welcome-box">
  <p class="title">Hello World</p>
  <p class="subtitle">
    <span class="small">This is a</span>
    <span class="big">test</span>
  </p>
</div>
</tempate>
<template lang="pug">
.welcome-box
  p.title Hello World
  p.subtitle
    span.small This is a
    span.big test
</template>

Bug Reporting System

추후 더 살펴보고 결정 내립니다. 찾아본 후보는 다음과 같습니다.

Other Libraries

Frontend - CSS

모든 프로젝트에는 디자인 가이드가 필요합니다. 단순히 문서 상으로 존재하는 가이드가 아니라, 바로 조합해서 사용할 수 있는 컴포넌트 수준으로 되어 있어야 합니다. 그래서 웹사이트를 개발할 때 기존의 가이드를 따라서 컴포넌트의 위치가 자연스레 정해지고, 버튼의 종류도 결정되며, 폰트의 사이즈나 색상도 맥락에 의해 큰 의심의 여지없이 결정되어야 합니다. 기존의 가이드를 벗어나는 경우가 발생하면 그때 다시 디자이너와 논의하던가 해서 추가적인 가이드를 만들어야 합니다. 이번에 진행할 프로젝트처럼 디자이너가 별도로 존재하지 않는 경우에는 이미 가이드가 잘 짜인 UI Framework 를 골라서, 그 Framework 가 가이드하는 방법에 맞춰 개발해야 합니다. 그래서 단순히 버튼, 텍스트, Radio 등의 UI 요소를 컴포넌트화 해놓은 library 가 아니라, 그걸 어떤 Grid system 으로 어떻게 구성해야 하는지까지 다루는 UI Framework 를 골라야 합니다.

Google 에서 내놓은 Material Design 방법론은 이번 프로젝트에서 따르지 않습니다. Material Design 은 실제 3D 인 물질세계에서의 경험을 UI System 으로 정리했습니다. 이는 단순히 x-y 좌표 상에 대한 이야기가 아니고 z 축까지 고려하는 등 보다 심도 깊은 개념인데, 잘 적용했을 시 좋은 사용성을 만들어 냅니다. 구글이 만든 Google Maps, Inbox 등이 Material Design 컨셉에 맞게 개발되었습니다. 그런데 소수의 운영자가 사용할 웹사이트를 단기간에 만들어야 하는 지금의 상황에서 제대로 적용하기엔 알아야 할 개념이 너무 방대합니다. Material Design 를 그런 개념과 철학을 모른 채 그냥 UI Component 정도 수준으로 이용할 거면, 차라리 기능적으로 풍성한 다른 UI Framework 를 선택하는 것이 낫다고 판단했습니다. 잘 알려진 몇몇 UI Framework 들은 단순 Form 요소에 관련된 컴포넌트뿐 아니라 ImageUploader, DateTimePicker 등 다양한 컴포넌트를 지원하기 때문에, 사실 그런 UI Framework 가 제공하는 컴포넌트를 잘 조합하는 것만으로도 (UI 측면에서) 웬만한 웹사이트는 다 개발할 수 있습니다.

UI Framework

https://github.com/vuejs/awesome-vue#frameworks 위 링크의 UI Framework 중에 인지도와 컴포넌트의 다양성, 사용될 예정인 이미지 업로더 컴포넌트의 퀄리티 등을 살펴보고 다음 네 가지를 추렸습니다.

  • http://element.eleme.io/

  • https://buefy.github.io

  • https://bootstrap-vue.js.org

  • https://semantic-ui-vue.github.io

  • 이미 널리 사용되고 있는 Semantic UI 를 Vue 를 위해 포팅한 semantic-ui-vue 를 들여다보았으나, 이미지 업로더가 없었습니다.

  • 그리고 bootstrap-vue 도 마찬가지였습니다.

  • element 는 애초에 Vue 를 위해 만들어진 만큼 다른 dependency 없이 완만한 learning curve 를 가지고 있고, 이미지 업로더 컴포넌트에 필요한 기능들이 잘 패키징 되어 있었고, Form Validation 을 위한 툴이 bootstrap-vue 나 buefy 에 비해 잘 갖추어져 있어서 form 을 많이 사용할 이 프로젝트에 적합하다 생각했습니다.

  • buefy 는 bulma 라는 css framework 의 VueJS 용 wrapper 인데, 잘 짜여진 api 를 갖추고 있는데다, 자체적인 form validation 기능은 적지만 VeeValidate 등과 더불어 사용하면 되고, 여러모로 다양한 UI Component 세트를 갖추고 있어서 buefy 로 선택했습니다.

Scoped Style

VueJS 에서는 Scoped Style 기능을 제공합니다. 해당 컴포넌트 안에만 영향을 끼치도록 scope 가 형성되기 때문에, 스타일 구문이 바깥에 영향을 주는 점에 대해 염려할 필요가 없습니다.

BEM

BEM Naming Convention 이라는 방식을 통해 element 의 class 이름을 지을 수 있습니다. 구조가 잘 짜인 Naming Convention 이긴 하지만, 이미 Scoped Style 을 통해 아주 작은 단위의 Scope 에 대해서만 style 코드를 작성하게 될 것이라, BEM 은 사용하지 않습니다. 다만 element 에 id 속성은 사용하지 않으며, class 이름은 camel-case 로 합니다.

살펴보기

SASS

CSS Preprocessor 로 SASS 를 사용합니다. 사실 Element UI 를 사용하기 때문에 CSS 코드를 작성할 일이 거의 없어야 하지만, 하게 된다면 SASS 문법으로 작성합니다. 이 또한 CSS 코드 양이 얼마 되지 않기 때문에 실질적으로는 Plain CSS 문법으로 작성하게 될 가능성이 높습니다. SASS 는 필요시 적용할 수 있는 문법 정도의 개념으로 포함시켰습니다.

CSS Linter

위와 같은 맥락으로 CSS 코드 양이 얼마 되지 않겠지만, 그래도 Linter 는 세팅해 놓습니다.

살펴보기 https://github.com/stylelint/stylelint

Common

break / continue

for loop 에서 break / continue 를 자제합니다. 코드 가독성을 떨어뜨릴 확률이 높습니다. 둘다 모두 array 의 filter 류의 함수를 통해 더 나은 가독성으로 구현할 수 있습니다.

살펴보기 https://hackernoon.com/rethinking-javascript-break-is-the-goto-of-loops-51b27b1c85f8

Early Return

함수 초반에 오류 체크를 하고 Early Return 을 할 수 있으면 합니다. 그리고 그 이후에는 함수 맨 마지막에만 한번 return 합니다. (중간에 return 하지 않습니다.)

살펴보기

Indentation Level

함수 내에 들여쓰기는 2 단계를 넘지 않도록 합니다. 그 이상으로 들여쓰기가 들어갔다면, 잘못 작성되었거나, 별도의 메소드로 extract 할 여지가 있다고 볼 수 있습니다.

살펴보기 https://hackernoon.com/how-to-make-your-code-clean-and-beautiful-5ff7aee03be6

IDE & Environment

Backend

RubyMine 을 사용합니다.

Rubocop 으로 Lint 가 될 테고, RubyMine 의 Action 창에서 Fix all auto-correctable RuboCop offenses 기능을 실행하면 auto-correct 가 됩니다. 해당 기능에 short-cut key 를 mapping 해두면 좋습니다.

git 의 pre commit hook 을 통해 Rubocop 의 lint 가 실패했을 시에는 commit 을 할 수 없도록 강제합니다. 이 hook 은 command line, RubyMine 모두에서 작동합니다.

Frontend

WebStorm 혹은 VSCode 를 사용합니다. 선호에 따라 선택할 수 있고, IDE 가 달라도 협업하는데 문제가 없도록 세팅합니다.

ESLint, TSLint 가 작동되고, WebStorm 기준으로는 Fix ESLint Problems 기능을 통해 auto-correct 할 수 있습니다.

마찬가지로 git 의 pre commit hook 을 통해 lint 가 실패했을 시에는 commit 을 할 수 없도록 강제합니다. 이 hook 은 command line, IDE 모두에서 작동합니다.

ESLint 가 권장하는 Rule 을 지켜주면서 auto-formatting 까지 해주는 방법은 아직까진 못 찾았습니다. 코딩을 하면서 혹은 커밋하기 전에 Linter 가 알려주는 내용에 대해 놓치지 않고 작업합니다.

Test Domain

로컬에 띄운 웹사이트를 브라우저에서 localhost 로 브라우징 하지 않고, 진짜 도메인으로 테스트할 수 있습니다. localhost 인 경우에 브라우저의 많은 기능 (cookie, sessionStorage, ...) 이 정상 작동하지 않습니다. 손쉽게는 http://readme.localtest.me/ 를 사용할 수 있고, https 가 필요하다면 https://github.com/localtunnel/localtunnel 를 사용할 수도 있습니다.

etc

Code Review

새로운 Framework 와 새로운 개념, 그리고 새로운 툴들을 가지고 만드는 프로젝트이기 때문에, 안정화되기까지 서로의 코드를 리뷰하며, 의도를 파악하고, Consensus 를 맞추는 기간이 필요하다고 생각합니다. 코드 리뷰의 강도나 프로세스에 대해서는 추가적인 논의가 필요합니다.

살펴보기 https://github.com/thoughtbot/guides/tree/master/code-review

Awesome Series

MUST READ

아래 링크는 이 문서에 지금까지 참조했던 링크 중에 중요한 것만 다시 추리고 몇 가지를 덧붙인 것입니다.

Backend

Frontend

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.