# Django + Bower + Grunt

# 상황

재고/창고관리 웹페이지를 만들려고 한다. 전체적으로 djangodocker로 돌아가고, 화면은 6개 정도 되는 크지 않은 admin페이지다. 나는 프론트엔드 담당이다.

# 생각하기

앞단을 짜는 것엔 두 가지 방법이 있다.

  1. django템플릿을 써서 서버사이드 렌더링을 한다.
  2. api를 받아와서 ajax로 만든다.

관리자 페이지니 봇이 긁어가야 할 필요도 없고, 이미 api들이 만들어지고 있는 상태라 2번을 사용하기로 했다. 기간이 2주뿐이라 조금이나마 익숙한 angular를 쓰기로 했다(리액트 써보고 싶다 힝).

# 구조 잡기

현재 디렉토리 구조는

  • api
    • migrations
    • serializers
    • test
    • views
    • (등등 django REST Framework기반의 api서버이다.)
  • conf
    • development
    • production
    • testing
  • etc
    • (docker들어가있음)
  • stock
    • (django 메인 앱)
    • init.py
    • settings.py
    • urls.py
    • wsgi.py
  • web
    • (여기다 웹 프론트를 짜면 된다)
  • manage.py 등 장고 관련 파일들

로 되어있다. 나는 web에다 프론트를 짜면 된다. angularjsone page web을 만들 예정이라 루트(/)로 접속했을 때 web/base.html로 연결되도록 stock/urls.py에 명시해주어야 했다.

# static file 사용하기

# urls.py

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    url(r'^$',
        TemplateView.as_view(template_name='base.html'),
        name='main'),
    url(r'^api/', include('api.urls')),
    url(r'^admin/', admin.site.urls),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

base.html을 템플릿뷰로 연결해주었다. 하지만 그냥 url만 적어주면 web폴더 내의 base.html은 템플릿파일로 인식을 못하기에 뒤에 static을 연결해준다.

# settings.py

TEMPLATES = [
    {
        ...
        'DIRS': [
            'web'
        ],
        ...
    },
]

...

STATICFILES_DIRS = [
    ("web", os.path.join(BASE_DIR, "web")),
]

settings.py TEMPLATES내부의 DIRS에 폴더명을 적어준다. 그리고 밑의 STATICFILES_DIRSweb이란 이름으로 BASE_DIRweb폴더를 연결해둔 패스를 지정해준다. 그 후로부턴 저 BASE_DIR/web폴더 내부 staticfile을 사용할 때 앞에 'web'이란 이름을 적어주면 된다.

# web/base.html

{% load staticfiles %}

base.html위에 staticfiles를 로드해주고

<link rel="stylesheet" href="{% static 'web/dist/css/base.css' %}">

불러올 땐, 폴더를 아까 정해준 이름(web)으로 호출하면 된다.

# 의존성 관리 - Bower

의존성 관리 툴은 bower와 npm을 사용하였다. 최상위 폴더에 bower 설정파일을 만들어준다.

# bower.json

{
  "name": "sandi",
  "dependencies": {
    "admin-lte": "latest",
    "fastclick": "latest"
  }
}

이름과 dependencies만 적어주었다. admin-lte라는 admin사이트 만드는 곳에 특화된 부트스트랩 템플릿과, 모바일에서 touch evnet를 도와주는 fastclick을 설치하였다. 콘솔에서 bower install하면 이들이 bower_componenets폴더 내에 설치된다. 이도 staticfile로 접근해야하니, settings.py에 한번 더 명시해준다.

# settings.py

STATICFILES_DIRS = [
    ("components", os.path.join(BASE_DIR, "bower_components")),
    ("web", os.path.join(BASE_DIR, "web")),
]

components란 이름으로 연결해주었다.

<link rel="stylesheet" href="{% static 'components/admin-lte/bootstrap/css/bootstrap.min.css' %}">

이는 아까와 같이 사용할 수 있다(web대신 componenets라고 명시)

# 프론트엔드 태스크 자동화 - grunt

태스크 자동화는 grunt로 하였다. 저번 프로젝트는 gulp로 했었는데, admin-lte가 준 grunt파일이 좋아보여 얘로 결정!

# web/package.json

{
  "name": "sandi",
  "version": "0.1.0",
  "repository": {
    "type": "git",
    "url": `https://github.com/haha`
  },
  "devDependencies": {
    "R2": "^1.4.3",
    "grunt": "~0.4.5",
    "grunt-contrib-clean": "^0.6.0",
    "grunt-contrib-csslint": "^0.5.0",
    "grunt-contrib-cssmin": "^0.12.2",
    "grunt-contrib-jshint": "^0.11.2",
    "grunt-contrib-less": "^0.12.0",
    "grunt-contrib-uglify": "^0.7.0",
    "grunt-contrib-watch": "~0.6.1",
    "grunt-cssjanus": "^0.2.4",
    "grunt-image": "^1.0.5",
  }
}

npm 설정파일을 만들어 필요한 grunt파일들을 넣어준다.

# web/Gruntfile.js

module.exports = function (grunt) {

  'use strict';

  grunt.initConfig({
    watch: {
      files: ["static/less/*.less", "build/less/skins/*.less", "static/js/app.js"],
      tasks: ["less", "uglify"]
    },
    /* LESS Compile */
    less: {
      development: {
        options: {
          compress: false
        },
        files: {
          "dist/css/base.css": "static/less/base.less",
        }
      },
      production: {
        options: {
          compress: true
        },
        files: {
          "dist/css/base.css": "static/less/base.less",
        }
      }
    },
    /* Javascript Uglify */
    uglify: {
      options: {
        mangle: true,
        preserveComments: 'some'
      },
      my_target: {
        files: {
          'dist/js/app.js': ['static/js/app.js']
        }
      }
    },
    /* Image Compression */
    image: {
      dynamic: {
        files: [{
          expand: true,
          cwd: 'static/img/',
          src: ['**/*.{png,jpg,gif,svg,jpeg}'],
          dest: 'dist/img/'
        }]
      }
    },

    // Validate JS code
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      core: {
        src: 'static/js/app.js'
      }
    },

    csslint: {
      options: {
        csslintrc: 'static/less/.csslintrc'
      },
      dist: [
        'dist/css/base.css',
      ]
    },

    /* Compression 전 이미지 삭제 */
    clean: {
      build: ["static/img/*"]
    }
  });

  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-image');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-csslint');

  grunt.registerTask('default', ['watch']);
};

web/static폴더에 less, js, img를 넣고, grunt를 돌리면 web/dist폴더 minify되고 컴파일되고 compression된 css, js, img가 들어가도록 해두었다.(+ validation)

# gitignore

dist

### Frontend ###
node_modules
bower_components

distribution에서 사용하는 dist폴더와, npm으로 설치한 node_modulesbower로 설치한 bower_components는 git에서 제외시켜뒀다. 이로서 깨끗한 깃헙이 되었다!!! >.<

# 돌리자!

다른 개발자들을 위해 README.md에 써준다.

bower install
cd web
npm install
grunt