django, bower, grunt로 사이트 구조잡기

상황

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

생각하기

앞단을 짜는 것엔 두 가지 방법이 있다.
1. django템플릿을 써서 서버사이드 렌더링을 한다.
2. api를 받아와서 ajax로 만든다.

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

구조 잡기

현재 디렉토리 구조는

- 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