gulp 모듈을 Mac OS에 설치하는 경우 책에서는 언급하지 않는 오류가 있다.



1. gulp 설치


npm config set prefix /usr/local

npm install gulp gulp-cli --global

npm install gulp gulp-cli --save


보통 예제들은 첫 번째 커맨드 없이 gulp 설치를 안내하는데 내 경우는 npm이 잘못 깔린 건지 명령만으로는 gulp가 정상적으로 작동하지 않아 별도로 명령을 추가했다. npm이 모듈을 설치할 때 강제로 해당 prefix를 이용하도록 한다. (Mac OS 키워드로 검색되는 결과가 많은 걸로 보아 OS의 특성인 것도 같다.)



2. gulpfile.js 작성


1
2
3
4
5
6
7
8
9
var gulp = require('gulp');
 
gulp.task('griting'function () {
    console.log('hello, gulp!')
});
 
gulp.task('default'function () {
    gulp.start('griting')
});
cs


걸프가 어떻게 작동하는지 코드만 봐도 짐작하리라 생각된다.

파일명을 바꾸지 않도록 주의한다.



3. gulp 실행


gulp


예제나 기타 웹 자료에서는 gulp {파일명} 인 경우가 많았으나 실제로 해당 커맨드를 실행하면 gulpfile.js의 포맷팅이 적절하지 않다고 출력된다. (이것도 OS의 문제인지, 내가 잘못 설치한 탓인지 짐작도 가지 않는다.)

실행 결과는 아래와 같다.


 


4. 복수의 css 파일 통합


걸프는 파이프/스트림을 활용하는 아키텍처의 비동기식 작업 실행기다. 스트림 기반의 빌드 시스템이라고도 하던데, 나는 책에서 언급한 복수의 css 파일을 통합해 min 버전을 출력하는 걸프 코드를 소개하려 한다.

코드 작성에 앞서 필요한 걸프 플러그인을 설치한다.


npm install gulp-stylus gulp-concat --save


설치가 끝났다면 임의의 styl 파일 두 개를 작업 경로/assets/ 아래에 작성하자. 스타일러스가 낯선 사람은 아주 간략한 수준의 css를 확장자만 styl로 바꾸어 작성하도록 한다. 나는 스타일러스가 낯설었기 때문에 아래와 같이 작성했다.


1
2
3
4
#table1 {
    text-align: center
    
}
cs

▲ ./assets/sample1.styl

1
#table2 {        text-shadow: 0ch}
cs


▲ ./assets/sample2.styl


공백 압축이 적절하게 이루어지는지 알고 싶었기 때문에 의도적으로 포맷팅을 적용하지 않았다.

다음은 두 개의 스타일러스를 css로 컴파일 후 하나의 min 버전 css로 출력하는 걸프 스크립트다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var gulp = require('gulp')
var stylus = require('gulp-stylus')
var concat = require('gulp-concat')
gulp.task('css'function () {
    gulp.src('./assets/*.styl')
        .pipe(stylus({
            compress: true
        }))
        .pipe(concat('main.min.css', {
            newLine: ' '
        }))
        .pipe(gulp.dest('./public/stylesheets'))
})
gulp.task('default'function () {
    console.log('test')
    gulp.start('css')
})
cs


▲ gulpfile.js


해당 코드는 assets 경로 아래 모든 styl 파일을 압축 후 public/stylesheets 경로 아래 main.min.css로 출력한다.

실행 결과로 생성된 main.min.css의 내용은 다음과 같다.


1
#table1{text-align:center} #table2{text-shadow:0}
cs


깔끔하게 압축된 것을 확인할 수 있다.



5. 파일 변경 감시 처리


보통 개발자라면 원본 파일 수정 후 gulp를 매번 직접 수행하고 싶지는 않을 것이다.

이제 작업 경로를 늘 감시하다 파일이 변경되면 자동적으로 원하는 태스크를 수행하는 걸프 코드를 작성해보자.


1
2
3
4
5
6
7
gulp.task('watch'function () {
    gulp.watch('./assets/*.styl', ['css'])
})
 
gulp.task('default'function () {
    gulp.start('watch')
})
cs


이제 gulp를 실행하면 종료하지 않는 이상은 프로세스가 계속 지정한 경로를 감시할 것이다.

상기 코드는 assets 아래에 위치한 styl 확장자를 가진 모든 파일의 변화를 감시하고, 변화가 있을 때마다 'css' 태스크를 실행한다.

개발자는 minify와 concat을 위해 최초의 스크립트만 작성하면 되는 것이다.

 



이전 회사에서 사이트 최적화 작업 때 min 파일을 수기로 만든 기억이 있는데 역시 개발자는 공부만이 답이라는 걸 알 수 있다. 지식이 없으면 손발이 고생하는 건 만고불변의 진리인가 싶다.


추가로 css를 대상으로 한 플러그인도 여럿 있는 듯하니 필요한 사람은 찾아보도록 하고 기타 걸프 상세는 공식 문를 참고하기를 추천한다. 언제든 역시 공식 레퍼런스가 최고다…. 책에서는 javascript 압축 예제도 안내하고 있으니 관심 있는 사람은 책도 읽어보자.





WRITTEN BY
Project JT
2명의 개발자가 팀 프로젝트를하며 사용한 기술들을 남기는 팀 블로그입니다.

,



참고 서적: 풀스택 개발자를 위한 MEAN 스택 입문

개발 환경: Mac OS


책을 읽고 관련한 정보가 잊혀지기 전에 간략하게 작성한다.






1. mongodb 설치


brew install mongodb

mongo --version > v4.0.1


로컬에 디비를 설치하지 않는 경우는 호스팅 사이트를 이용할 수 있다.

내 경우는 책에서 추천한 mlab에서 제공하는 무료 500mb 플랜을 받아 연결했다.

몽고디비 인스턴스 관리 외에 데이터 관리를 위한 GUI도 제공하기 때문에 여러모로 쉘 작업보다 편리하다.




2. mongoose 설치


전역으로 설치하지 않는 경우는 작업 경로로 이동해서 작업해야 하며 전역으로 설치시에는 --save 대신 -global(-g) 옵션이 필요하다.


npm install mongoose --save




3. mongoose 사용


몽구스 사용은 스키마 정의부터 시작된다. 스키마를 정의하고 나면 해당 스키마를 통해 모든 CRUD 작업을 코드에서 통제할 수 있다.

(개인적으로는 java 웹 애플리케이션에서 작성하던 Entity와 유사하게 여겨졌다. 다만 Entity는 단순히 테이블 명세를 객체화 하기 위한 POJO 클래스에 지나지 않아 정보를 담는 것 외에는 특별한 일을 하지 않는다.)

책에서 작성한 HR앱을 베이스로 한 간단한 스키마 예제를 첨부한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var EmployeeSchema = new Schema({
    id: {
        type: String,
        required: true,
        unique: true
    },
    name: {
        first: {
            type: String,
            required: true
        },
        last: {
            type: String,
            required: true
        }
    },
    team: {
        type: Schema.Types.ObjectId,
        ref: 'Team'
    },
    image: {
        type: String,
        default'images/user.jpg'
    },
    address: {
        lines: {
            type: [String]
        },
        city: {
            type: String
        },
        zip: {
            type: Number
        }
    }
})
 
module.exports = mongoose.model('Employee', EmployeeSchema)
cs


▲ models/EmployeeSchema.js


코드는 매우 쉽고 직관적이다. 언급하고 넘어갈 만한 부분은 연관성을 나타낸 team에 대한 정보다.

Schema.Types.ObjectId는 team을 위한 값이 몽고디비의 유일한 식별자라는 뜻으로 team을 위해 사용할 모델명은 Team임을 알 수 있다. TeamSchema의 정의는 아래와 같다.


1
2
3
4
5
6
7
8
9
10
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var TeamSchema = new Schema({
    name: {
        type: String,
        required: true
    }
})
 
module.exports = mongoose.model('Team', TeamSchema)
cs


▲ models/TeamSchema.js


모델 정의 때 사용된 값이 ref에서 사용되는 것을 확인할 수 있다. 이제 스키마를 통해 간략한 CRUD 코드를 작성해 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var mongoose = require('mongoose')
var Team = mongoose.model('Team')
 
exports.findOne = findOne
exports.insert = insert
exports.update = update
exports.remove = remove
 
function findOne(team, callback) {
    Team.findOne(team, function (err, data) {
        callback(data)
    })
}
 
function insert(team, callback) {
    Team.create(team, function (err, target) {
        callback(null'[inserted]')
    })
}
 
function update(team, changing, callback) {
    team.name = changing
    Team.update(team, function (err, target) {
        callback(null'[updated]')
    })
}
 
function remove(team, callback) {
    Team.remove(team, function (err, target) {
        callback(null'[deleted]')
    })
}
cs


lib/team.js


TeamSchema와 연관된 CRUD 코드를 모아둔 서비스다.

예제 코드이므로 별도의 에러 처리는 모두 생략했으나 실제 서비스에서는 반드시 작성하도록 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require('./models/team')
var mongoose = require('mongoose')
var Team = mongoose.model('Team')
var teamService = require('./lib/team')
 
var dbUrl = 'mongodb://{url}:{port}/{db name}'
mongoose.connect(dbUrl, function (err) {
    if (err) {
        return console.log('[DB 연결 실패] ' + err)
    } else {
        console.info('[DB 연결] ' + Date.now())
 
        var data = { name'Development' }
        teamService.insert(data, logger)
        
        // teamService.findOne(data, function (record) {
        //     console.info('[updating...]')
        //     console.info('[before] ' + record.name)
        //     teamService.update(record, 'new Development', logger)
        //     console.info('[after] ' + record.name)
        // })
    }
})
 
function logger(err, act) {
    console.log(act)
}
 
process.on('SIGINT'function () {
    mongoose.connection.close(function () {
        console.info('[DB 연결 해제] ' + Date.now())
        process.exit(0)
    })
})
cs


▲ 서비스 호출 예시


현재 코드에서는 새로운 행을 추가하는 코드만 활성화되어 있다.

수정, 삭제에 관한 코드 호출과 해당 CRUD를 제공하는 RestfulAPI를 작성은 이제까지의 포스팅을 참고해 작성할 수 있다.




몽고디비가 아닌 다른 데이터베이스(관계형을 아우르는)를 연결하고 싶은 경우는 관련한 노드 모듈을 찾아볼 것을 추천한다. 책에서는 별도로 MySQL을 언급하고 있다.



WRITTEN BY
Project JT
2명의 개발자가 팀 프로젝트를하며 사용한 기술들을 남기는 팀 블로그입니다.

,



참고 서적: 풀스택 개발자를 위한 MEAN 스택 입문

개발 환경: Mac OS


책을 읽고 관련한 정보가 잊혀지기 전에 간략하게 작성한다.






1. 익스프레스 생성기 설치 및 실행


npm install express-generator -g


-g는 global을 의미한다. 익스프레스 생성기 설치가 완료되었다면 작업 경로로 이동 후 express 를 실행한다.



▲ express 실행 결과


bin: 명령행에서 수행할 의도가 있는 파일


npm start 혹은 node ./bin/www 로 서버를 시작할 수 있다.




2. 기존 노드 앱 익스프레스 적용


이전 포스팅 3번 항목에서 기초적인 라우팅을 제공하는 간단한 노드 앱을 작성했었다.

해당 코드에 익스프레스를 적용해 리팩토링한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var express = require('express')
var app = express()
 
app.get('/employees'function (req, res, next) {
    res.send('직원 목록')
})
 
 
app.get('/employees/:employeeId'function (req, res, next) {
    res.send('직원 코드 ' + employeeId + '의 정보')
})
 
var server = app.listen(1337function () {
    console.info('Server started on port 1337')
})
cs


▲ express_server.js


리팩토링이 끝났다면 node express_server.js 로 실행해보자. 정적 파일 외에는 이전과 동일한 결과를 얻을 것이다.

익스프레스는 이 외에 POST, DELETE, PUT에 해당하는 모듈도 제공한다.

express 실행 후 생성된 bin/www와 app.js를 보며 우리가 작성한 express_server.js가 npm start를 통해 적용되도록 작성해볼 것을 권한다.




3. 라우트


라우트는 http 동사와 경로를 포함한다. 상기 예시 코드에서 쓴 바와 같이 정적 경로만으로 이루어질 수도 있으며 인자 혹은 옵션 인자를 포함하는 경우도 있다.


  • 정적 경로 예시: /employees > http://.../employees
  • 인자 경로 예시: /employees/:employeeId > http://.../employees/1003
    함수 내에서 옵션 값은 req.params를 통해 접근할 수 있다. 상기 예시의 경우 인자 employeeId에 접근하기 위해서는 req.params.employeeId를 호출한다.
  • 옵션 인자 경로 예시: /employees/:employeeId/:subEmployeeId? > http://.../employees/1003/12 OR http://.../employees/1003
    다만 옵션 인자는 문서화가 어렵고 다양한 인자 옵션 기반 탓에 디버깅이 어려우므로 사용을 권장하지 않는다.

이외 라우트에는 정규 표현식 또한 사용 가능하다. 상기 예시 중 인자 경로 예시를 정규 표현식으로 표현하면 /^\/employees\/(\w+)$/i가 될 것이다. 정규 표현식을 사용할 때는 미들웨어 함수에서 해당 값에 접근하기 위해 ()를 사용한 캡처 그룹을 생성해야 한다. 익스프레스는 모든 라우트 정의를 정규 표현식으로 변환한다.





WRITTEN BY
Project JT
2명의 개발자가 팀 프로젝트를하며 사용한 기술들을 남기는 팀 블로그입니다.

,