middleman은 SSG(Static Site Generator) 프레임워크입니다.
가장 많이 알려진 SSG 프레임워크로는 jekyll이 있습니다. github에서 jekyll 엔진을 기본으로 제공하면서 유명해졌죠.
SSG 프레임워크의 주기능은 주로 템플릿 관리에 집중되있습니다. 따라서 블로그나 mini-project에서 많이 활용됩니다.
 
본인도 jekyll을 많이 사용해봤지만 템플릿관리 이상으로 활용하기엔 아쉬운점이 많았습니다.
하지만 middleman 을 써보며 jekyll에서 아쉬웠던 점들을 많이 해소할 수 있었습니다.
middleman은 jekyll보다 더 풍부한 기능들을 제공하고 Custmizing이 쉬운 장점을 갖고 있습니다.
가장 매력적인건 다양한 전처리 파일 build를 지원합니다.
 
지금부터 middleman을 저의 프론트엔드 개발 프로세스에 활용한 사례를 소개드립니다.
 
 

내가 생각하는 프론트엔드 개발 프로세스

 
최근 프론트엔드 개발 프로세스는 많이 복잡해졌습니다.
프론트엔드 개발 프로세스는 “관리의 효율성”, “성능최적화”, ” 커뮤니케이션”등을 모두 만족해야하므로
배포할 수 있는 소스를 만들기까지 많은 단계를 거칩니다.
 
이전엔 스프라이트 이미지제작, 공통 소스 분리, 소스 minify 등등…꽤 많은 것들을 수작업으로 했습니다.
최근엔 ruby나 nodeJs기반의 전처리기들이 발달하여 산출물을 보다 논리적으로 관리할 수 있습니다.
제가 생각하는 프론트엔드 개발 프로세스는 대충 이렇습니다.
1
 
html, css, js, image를 먼저 전처리기 파일로 생성 후 빌드하여 최종산출물을 생성합니다.
이렇게 전처리기를 활용하면 원하는 환경에 맞추어 산출물의 분리와 병합, 연산과정을 쉽게 할 수 있습니다.
 
저는 이 과정을 프론트엔드 개발 프로세스의 base로 생각하고 한단계 더 나아가 산출물을 UIO(UI Object)단위로 관리할것을 고민했습니다.
가장 이상적인 개발환경이라면 모든 산출물을 UIO단위로 잘게 쪼개고 이 조각들이 들어가고 빠지는 것을 자유자재로 컨트롤할 수 있어야겠죠. 마치 워드프레스처럼요…
UIO단위의 개발 프로세스를 그려봤습니다.
 
middleman.002
 
1) 화면설계서를 이해하고
2) 공통 레이아웃과 UI를 분리하고
3) 각 UI의 key/value값들은 json/yaml 형식의 데이터 파일로 분리하고
4) UIO단위로 전처리 파일을 생성하고
5) 이를 빌드하여
6) 최종산출물을 생성하고
7) UIO별로 볼 수 있는 페이지와 각 UI에 대한 매뉴얼 페이지를 만든다.
* 모든 산출물은 UIO페이지로부터 자동으로 생성된다.
 
제가 너무 오버하는건가요? 아니요! 대형 프로젝트일수록 UI 관리는 더 명확해져야합니다.
문제는 저 많은 build 과정을 어떻게 해결할것인가입니다.
 
 

middleman으로 관리해보자

 
middleman은 이런 과정을 한방에 해결해 줍니다.
위 그림에서 적색영역이 middleman이 동작하는 단계이고 build 명령어 한방으로 모든 과정을 처리합니다.
저는 제가 담당하는 SmartEditor 개발환경에 middleman을 적용했습니다. 지금부터 적용 과정과 함께 middleman 기능을 설명드릴게요.
 

1. 간단소개

http://middlemanapp.com/
1
– Static Site Generator
– Ruby 기반
– 템플릿 관리를 위한 모델링
– layout분리, include, 다양한 연산 문법 제공
– Sass / haml / slim / Coffee Script / 등의 전처리 파일의 build 지원
– yaml / json 등 데이터 파일 지원
 

1) 설치

ruby가 설치되있다면 middleman과 bundler만 설치하면 됩니다.

gem install middleman
gem install bundler

http://middlemanapp.com/basics/getting-started/
 

2) 디렉토리 구조

mymiddlemansite/
+-- .gitignore
+-- Gemfile                   # bundler 설치 파일
+-- Gemfile.lock
+-- config.rb                 # 사이트 설정
+-- source                    # 전처리 파일
    +-- images
    +-- index.html.erb          # index 파일 (erb, haml, slim등 포맷 지원)
    +-- javascripts
    +-- layouts                 # 레이아웃 폴더
    ¦   +-- layout.erb
    +-- partials                # 인쿠르드 파일
    +-- stylesheets
+-- build                     # build 후 생성되는 파일
+-- data                      # yml, json 데이터 파일

http://middlemanapp.com/basics/directory-structure/
 

2. 적용할 기술들

toolbar
스마트에디터는 많은 버전을 갖고 있습니다. 또한 한/중/일/영 다국어 버전을 제공합니다.
비슷해 보이지만 각 버전별로 기능이 다르고 구조가 조금씩 바뀝니다.
각 버전별로 산출물을 관리하는건 매우 번거롭습니다. 하나의 소스에서 다양한 버전으로 패키징될 수 있는 구조를 만들고 싶습니다.
따라서 middleman기반에서 다음 기술들을 적용할 것입니다.
 
– Json : 스마트 에디터 툴바에 들어가는 클래스 / 데이터값 등을 json파일로 관리한다
– Haml : html 전처리기
– Sass : css 전처리기
– Compass : 분리된 이미지들을 Sprite Image로 생성
– Middleman : 위 과정을 일괄적으로 build하여 html파일을 생성한다. 스마트 에디터는 여러가지 버전이 있는데 한개 파일에서 여러버전의 html을 생성하는 구조를 만든다.
 

3. Layout & include

콘텐츠와 레이아웃을 따로 분리할 수 있습니다.
(참조 : http://middlemanapp.com/basics/templates/)

<!-- layout.html.erb -->
<html>
<head><title>SmartEditor2</title></head>
<body>
<%= yield %>
</body>
</html>

include는 partials 명령을 사용합니다.
로컬 파라미터를 넘겨 마크업을 다이나믹하게 넣을 수도 있습니다.

.se2_input_area.husky_seditor_editing_area_container
    = partial "partials/reedit_msg", :locals => { :ver => locals[:ver], :lang => locals[:lang] }
    = partial "partials/editor_mark", :locals => { :ver => locals[:ver], :lang => locals[:lang] }
    = partial "partials/input_wysiwyg", :locals => { :ver => locals[:ver], :lang => locals[:lang] }

 

4. Haml 파일 생성

ROR(Ruby on rails) 전용 파일 포맷을 모두 쓸 수 있습니다.
haml 로 표현하면 태그와 속성을 뚜렷하게 구분할 수 있고 태그의 depth 구분에 좋습니다.
필수는 아니며 erb 파일 포맷으로 사용하면 네이티브 HTML 코드로 사용할 수 있습니다.
[ 변환전 ]

/ SE2 Markup Start
#smart_editor2
    #smart_editor2_content
        %a.blind{:href => "#se2_frame"} 글쓰기영역으로 바로가기
        .se2_tool#se2_tool

 
[ 변환후 ]

<!-- SE2 Markup Start -->
<div id='smart_editor2'>
    <div id='smart_editor2_content'>
        <a class='blind' href='#se2_frame'>글쓰기영역으로 바로가기</a>
        <div class='se2_tool' id='se2_tool'>

 

5. JSON 데이터 파일 정리

에디터 툴바버튼에 반복적인 패턴으로 들어가는 클래스와 마크업을 json 파일로 구조화 시킵니다.
공통으로 쓸 마크업에 json파일을 loop 돌려 전체 마크업을 완성합니다.
[ text_toolbar.json ]

{
    "se2_font_type": {
        "font_name": {
            "li": "husky_seditor_ui_fontName",
            "button_class": "se2_font_family",
            "span_class": "husky_se2m_current_fontName",
            "text": {
                "ko_KR": "글꼴",
                "ja_JP": "フォント",
                "zh_CN": "字体",
                "en_US": "Font"
            },
            "keyboard": false,
            "layer": true,
            "new": false
        },
        "font_size": {
            "li": "husky_seditor_ui_fontSize",
            "button_class": "se2_font_size",
            "span_class": "husky_se2m_current_fontSize",
            "text": {
                "ko_KR": "크기",
                "ja_JP": "文字サイズ",
                "zh_CN": "字号大小",
                "en_US": "Letter Size"
            },
            "keyboard": false,
            "layer": true,
            "new": false
        }
    },
/* 중략 */
}

[ _text_toolbar.haml ]

%ul{class: "se2_font_type"}
    - text_tools.se2_font_type.each do |edit|
        %li{ class: edit[1].li }
            %button{ class: edit[1].button_class, title: edit[1].text[locals[:lang]] }
            %span{ class: edit[1].span_class != nil ? edit[1].span_class : "_buttonRound" }
                = edit[1].keyboard != false ? "#{edit[1].text[locals[:lang]]} #{edit[1].keyboard}" : "#{edit[1].text[locals[:lang]]}"

 

6. 변수 & 연산자 사용

마크업 구조가 항상 일정하지 않으므로 변수나 연산자로 구분해줘야합니다.
문법이 Ruby 기반이고 이걸 haml문법으로 표현해야해서 Ruby언어를 모르시는 분들은 처음에 다양한 삽질을 감수하실 수도 있습니다.
[ _text_toolbar.haml ]

- data.version[:"#{locals[:ver]}"].each do |groups|
    %ul
        - data.version[:"#{locals[:ver]}"][:"#{groups[0]}"].each_with_index do |tools, x|
            - text_tools.edit_option.each_with_index do |edit, i|
                - if tools == i
                    - if groups[1].length != 1
                        - if x == 0
                            - round = "first_child"
                        - if x == groups[1].length-1
                            - round = "last_child"
                    - else
                        - round = "single_child"
                    %li{ class: "#{edit[1].li} #{round}" }
                        - if i != 4 && i != 5
                            %button{ class: edit[1].button_class, title: edit[1].text[locals[:lang]] }
                                %span{ class: edit[1].span_class != nil ? "#{edit[1].span_class} tool_bg" : "tool_bg" }
                                    = edit[1].keyboard != false ? "#{edit[1].text[locals[:lang]]} #{edit[1].keyboard}" : "#{edit[1].text[locals[:lang]]}"
                            - if edit[1].layer != false
                                = partial "partials/layer/text_toolbar/#{edit[0]}", :locals => { :ver => locals[:ver], :lang => locals[:lang] }
                        - else
                            %span{ class: edit[1].pair1, style: "background-color:#000" }
                            %span{ class: edit[1].pair2.span_class }
                                %button{ class: edit[1].pair2.button_class }
                                    %span= edit[1].pair2.text[locals[:lang]]
                            %span{ class: edit[1].pair3.span_class }
                                %button{ class: edit[1].pair3.button_class }
                                    %span.tool_bg= edit[1].pair3.text[locals[:lang]]
                                - if edit[1].layer != false
                                    = partial "partials/layer/text_toolbar/#{edit[0]}", :locals => { :ver => locals[:ver], :lang => locals[:lang] }

 

7. Sass

전처리용 파일 디렉토리에 생성될 css 구조에 맞추어 scss를 배치하기만 하면 자동으로 build됩니다.
단 디렉토리명이 “sass”일 경우 build하지 않습니다. 따라서 sass 디렉토리엔 참조할 scss만 배치합니다.
[ 변환전 ]
/source/css/sass/*.scss
/source/css/*.scss
[ 변환후 ]
/build/css/*.css
2
 
 

8. Sprite images

Compass엔진으로 각각의 이미지를 스프라이트 이미지로 병합할 수 있습니다. 또한 background-position값도 자동으로 계산 후 출력해줍니다.
먼저 config.rb 에 이미지 경로 설정을 합니다.

# 이미지 경로
http_path = "/"
images_dir = "img"
relative_assets = true
# 빌드 설정
configure :build do
    # 상대 경로
    activate :relative_assets
end

scss 파일에 스프라이트 이미지로 변환할 이미지 디렉토리를 지정합니다.

/* CSS Sprite */
$btns-sprite-dimensions: true;    // 이미지 간격설정
@import "btns/*.png";             // 변환할 이미지 디렉토리 지정
@include all-btns-sprites;        // 스프라이트 이미지 경로와 background-position 값 출력

build후 다음과 같이 출력됩니다.

background-image: url("../img/btns-s292bb24f05.png");
.se2_apply{background-position:-20px -80px}
.se2_cancel{background-position:-40px -100px}
.se2_delete{background-position:-60px -150px}
.se2_close{background-position:-120px -200px}

 

9. Dynamic Pages

build할 파일에 대한 전처리 파일을 1:1로 만들지 않아도 공통파일에서 특정 변수를 받아 페이지를 생성해줍니다.
[ config.rb ]

["full", "basic", "light", "open"].each do |ver|
    if ver != "basic" && ver != "light"
        ["ko_KR"].each do |lang|
            proxy "/SmartEditor2Demo_#{ver}_#{lang}.html", "/SmartEditor2Demo.html", :locals => { :ver => ver, :lang => lang }, :ignore => true
        end
    else
        ["ko_KR", "ja_JP", "zh_CN", "en_US"].each do |lang|
            proxy "/SmartEditor2Demo_#{ver}_#{lang}.html", "/SmartEditor2Demo.html", :locals => { :ver => ver, :lang => lang }, :ignore => true
        end
    end
end

[ Output Files ]
3
 

10. Server

middleman server --verbose      # verbose : 에러 발생시 로그 노출

명령하나면 로컬환경에 서버가 돌아갑니다.
가장 매력적인 것은 전처리 파일들을 build하지 않고도 파싱할 수 있습니다.
전처리 파일을 수정하면서 실시간으로 확인하고 싶을 경우 livereload 를 활성화 하면 됩니다.

# config.rb
activate :livereload

 

11. build

middleman build

build 명령어 하나면 위에서 설명했던 모든 전처리 단계들을 자동을 처리 후 최종 산출물은 정적인 html파일로 생성해줍니다.
build엔 다양한 옵션이 있습니다.
전처리 파일의 build는 기본이고 deploy옵션을 사용하면 git 저장소에 올릴 수 있습니다.
(참고 : https://github.com/karlfreeman/middleman-deploy)
build 옵션만 잘 설정해도 grunt 기능을 대체할 수 있습니다.
결과적으로 이 복잡한 개발과정들이 build명령어 하나면  끝!
middleman.003
 
 

마치며…

 
middleman은 템플릿 관리를 위해 모델링된 툴이라 기능들이 직관적이고 간편했습니다.
또한 다양한 전처리 build를 지원하므로 grunt 없이도 자동화처리가 가능했습니다.
 
위 개발환경이 모든 프로젝트에 적합할 수 없을거 같구요 규모에따라 부분별 단계를 생략할 필요도 있습니다.
다양한 프로젝트에 맞춰 middleman 환경을 모델링 해놓으면 좀더 확장성을 더 높일 수 있을거 같습니다.
 
개발환경에 대한 고민은 누구나 갖고 있을거라 생각합니다. 특히 대형프로젝트일수록 효율적 관리의 중요성은 더욱 높아지겠죠.
꼭 middleman이 아니더라도 각자 갖고있는 노하우는 다양할 것입니다.
(본인은 예전에 프론트엔드 개발을 위해 Tomcat + Apache를 올려놓고 개발했던…)
어떤 방식을 사용하던 제가 생각하는 좋은 개발환경은 아래 3가지 조건을 갖추는 것입니다.
– readability (가독성)
– efficiency (효율성)
– maintainability (유지보수성)
 
말이 너무 길었네요. ㅎㅎ 이만 포스팅을 마칠게요.


0개의 댓글

답글 남기기

아바타 플레이스홀더

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다