반응형


ORM이란?

ORM(Object Relational Mapping)은 application과 Database사이를 맵핑시켜주는 도구이다. 한층더 추상화된 layer에서 Database에 대한 작업을 할 수 있게 해준다. ORM을 사용함으로써 얻는 장단점(pros and cons)은 다음과 같다.

1) Pros of ORM

  • 특정 DBMS에 종속되지 않는다.
  • SQL문이 코드에 들어가지 않아 깔끔한 코드를 유지할 수 있다.
  • ORM이 nesting데이터를 바인딩해준다.

2) Conf of ORM

  • RAW query에 비해 performance가 느리다.
  • Query tuning이 힘들다.
  • 서비스가 복잡해 질수록 ORM으로 할 수 있는 작업의 범위에 한계가 있다.

ORM에 대한 정보는 이곳링크를 통해 더 알아볼 수 있다. What is ORM? / Pros and Cons



Sequelize? Promise!

ORM의 종류는 여러가지가 있지만 Nodejs에서 가장 많이 사용되고 있는 ORM은 Sequelize다. Sequelize는 PostgreSQL, MySQL, MariaDB, SQLite, MSSQL을 지원하고 트랜잭션이나 relation, read replication등도 지원한다. 복잡한 쿼리에 사용될 부분들에 대해서는 오픈된 이슈들이 꽤 있고 지금도 활발하게 pull request나 commit이 이뤄지고 있다. 그리고 가장 큰 특징은 Promise를 기본으로 동작한다는 것이다. Promise는 Promise/A+ 로 불리는 spec에 따라 정의된 비동기작업 제어방식이다. ES6에는 native로 Promise가 포함되었다.

Promise의 장점은 다음과 같다.

  • 복잡한 비동기 코드를 깔끔하고 쉽게 만들 수 있는 방법을 제공한다.
  • Chaining 을 통해 값을 전달하거나 연속된 일련의 작업을 처리할 수 있다.
  • Error handling에 대한 처리를 깔끔하게 할 수 있다.

Promise를 구현한 라이브러리에는 대표적으로 QRSVPbluebird가 있다. Sequelize는 이중에서도 bluebird 라이브러리를 살짝 수정한 버전을 사용하고 있다. Promise를 비동기작업을 제어하는 방식으로 사용하는 만큼 Promise에 대해 알고 있는 부분이 많다면 Sequelize의 이용도 한결 수월해진다. Promise에 대해 더 알아보고 싶다면 다음을 참조하자. Javascript Promisehttps://www.promisejs.orgES6 Promise , Awesome-promise



테이블 생성 예


  • publisher 테이블 - 출판사에 관한 정보
  • books테이블 - 책에 대한 정보
  • user테이블 - 유저에 대한 정보
  • rent_history테이블 - 대여내역에 관한 정보




Setting up a connection

Sequelize의 설치는 npm install sequelize로 간단히 할 수 있다. 또한 Sequelize에서 postgres에 대한 작업을 하기 위해서는 추가로 pg, pg-hstore를 설치해야하므로 두개 모듈도 npm으로 설치하도록 하자. 설치 이후에는 서버가 구동될때 DB와 Connection을 맺도록 설정해야 한다. Connection에 대한 정보는 Sequelize객체를 생성할 때 parameter로 들어간다.

new Sequelize(database, [username=null], [password=null], [options={}])

var sequelize = new Sequelize('postgres://sequelize:1234@localhost/sequelize');
var sequelize = new Sequelize('sequelize', 'sequelize', '1234', {
    host: 'localhost',
    dialect: 'postgres'
});

connection연결은 위와 같이 두가지 방법 모두 가능하다.


Model Define

Sequelize에서 Model은 Database공간의 Table의 Schema를 표현하는 수단이다. Table Schema에 대응되는 Model을 정의한 이후에 실제로 메모리 공간에 올려 캐쉬하려면 import를 해야하는데 import는 바로 다음에 알아보도록하자. Model에 대한 정의는 Sequelize의 define 메소드를 이용한다. 예시는 다음과 같다.

sequelize.define('Publisher', {
    pub_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
    name: {type: DataTypes.STRING(32), allowNull: false},
    established_date: {type: DataTypes.DATE, defaultValue: DataTypes.NOW}
}, {
    classMethods: {},
    tableName: 'publisher',
    freezeTableName: true,
    underscored: true,
    timestamps: false
});

define 메소드의 첫번째 파라미터는 model의 name이다. 두번째 파라미터가 실제로 Table Schema와 맵핑되는 정보이다. 즉 table의 각 column들에 대한 schema를 표현한다. 대표적인 설정 값들 몇개를 알아보자.

  • type : Data type
  • primaryKey : 기본키 인지 아닌지 설정 (default: false)
  • autoIncrement : SERIAL(auto increment)인지 아닌지 (default: false)
  • allowNull : NOT NULL 조건인지 아닌지 (default: true)
  • unique : Unique조건인지 아닌지에 대한 옵션. column하나로만 이루어진 unique라면 true/false로 지정한다. 복수개의 column이라면 동일한 문자열을 각 column의 unique속성에 넣어준다.
  • comment : column에 대한 comment
  • validate : 각 column에 대한 validation check옵션을 넣어준다.

세번째 파라미터는 config 옵션이 들어간다. 대표적인 옵션은 이와같다.

  • timestamps : Sequelize는 테이블을 생성한 후 자동적으로 createdAt, updatedAt column을 생성한다. Database에 해당 테이블이 언제 생성되었고 가장 최근에 수정된 시간이 언제인지 추적할 수 있도록 해준다. 기능을 끄려면 false로 설정한다.
  • paranoid : paranoid가 true이면 deletedAt column이 table에 추가된다. 해당 row를 삭제시 실제로 데이터가 삭제되지 않고 deletedAt에 삭제된 날짜가 추가되며 deletedAt에 날짜가 표기된 row는 find작업시 제외된다. 즉 데이터는 삭제되지 않지만 삭제된 효과를 준다. timestamps 옵션이 true여야만 사용할 수 있다.
  • underscored : true이면 column이름을 camalCase가 아닌 underscore방식으로 사용한다.
  • freezeTableName : Sequelize는 define method의 첫번째 파라미터 값으로 tablename을 자동변환하는데 true이면 이작업을 하지 않도록 한다.
  • tableName : 실제 Table name
  • comment : table 에 대한 comment




Data Type

Sequelize에서 지원하는 DataType은 다음과 같다. (sequelize 공식 사이트에서 발췌)

Sequelize.STRING                      // VARCHAR(255)
Sequelize.STRING(1234)                // VARCHAR(1234)
Sequelize.STRING.BINARY               // VARCHAR BINARY
Sequelize.TEXT                        // TEXT

Sequelize.INTEGER                     // INTEGER
Sequelize.BIGINT                      // BIGINT
Sequelize.BIGINT(11)                  // BIGINT(11)

Sequelize.FLOAT                       // FLOAT
Sequelize.FLOAT(11)                   // FLOAT(11)
Sequelize.FLOAT(11, 12)               // FLOAT(11,12)

Sequelize.REAL                        // REAL        PostgreSQL only.
Sequelize.REAL(11)                    // REAL(11)    PostgreSQL only.
Sequelize.REAL(11, 12)                // REAL(11,12) PostgreSQL only.

Sequelize.DOUBLE                      // DOUBLE
Sequelize.DOUBLE(11)                  // DOUBLE(11)
Sequelize.DOUBLE(11, 12)              // DOUBLE(11,12)

Sequelize.DECIMAL                     // DECIMAL
Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)

Sequelize.DATE                        // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.BOOLEAN                     // TINYINT(1)

Sequelize.ENUM('value 1', 'value 2')  // An ENUM with allowed values 'value 1' and 'value 2'
Sequelize.ARRAY(Sequelize.TEXT)       // Defines an array. PostgreSQL only.

Sequelize.JSON                        // JSON column. PostgreSQL only.
Sequelize.JSONB                       // JSONB column. PostgreSQL only.

Sequelize.BLOB                        // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny')                // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)

Sequelize.UUID                        // UUID datatype for PostgreSQL and SQLite, CHAR(36) BINARY for MySQL (use defaultValue: Sequelize.UUIDV1 or Sequelize.UUIDV4 to make sequelize generate the ids automatically)

Sequelize.RANGE(Sequelize.INTEGER)    // Defines int4range range. PostgreSQL only.
Sequelize.RANGE(Sequelize.BIGINT)     // Defined int8range range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DATE)       // Defines tstzrange range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DATEONLY)   // Defines daterange range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DECIMAL)    // Defines numrange range. PostgreSQL only.

Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // Defines array of tstzrange ranges. PostgreSQL only.

INTEGER, BIGINT, FLOAT, DOUBLE type에는 unsigned와 zerofill에 대한 옵션도 지정할 수 있다

Sequelize.INTEGER.UNSIGNED              // INTEGER UNSIGNED
Sequelize.INTEGER(11).UNSIGNED          // INTEGER(11) UNSIGNED
Sequelize.INTEGER(11).ZEROFILL          // INTEGER(11) ZEROFILL
Sequelize.INTEGER(11).ZEROFILL.UNSIGNED // INTEGER(11) UNSIGNED ZEROFILL
Sequelize.INTEGER(11).UNSIGNED.ZEROFILL // INTEGER(11) UNSIGNED ZEROFILL

zerofill은 남는 자릿수를 0으로 채우는 방식이다. 예를 들면 INTEGER(4).ZEROFILL type인 column에 값이 ‘4’가 들어갈경우 0004로 표기되는 방식이다. model정의와 Datatype에 대한 정보는 이곳에서 볼 수 있다. sequelize model definitionsequelize data types

지금까지 Sequelize를 사용하기에 앞서 ORM, Promise에 대한 원론적인 이야기와 함께 Sequelize 설정과 간단한 옵션에 대한 부분을 알아보았다. 다음 챕터에는 실제로 Sequelize가 application에서 어떻게 쿼리를 수행하고 리턴하는지에 대한 부분을 간단한 예제와 함께 살펴보자.



Sync

Sequelize에서는 INSERT, SELECT, DELETE, UPDATE와 같은 DML뿐만 아니라 DDL문도 지원하는데 model에 정의된 스펙을 기준으로 Database의 테이블들을 동기화할 수 있다. 그때 사용하는 메소드가 sync메소드이다. 첫 번째 챕터 model define에 관한 예제였던 publisher model내역이다.

sequelize.define('Publisher', {
    pub_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
    name: {type: DataTypes.STRING(32), allowNull: false},
    established_date: {type: DataTypes.DATE, defaultValue: DataTypes.NOW}
}, {
    classMethods: {},
    tableName: 'publisher',
    freezeTableName: true,
    underscored: true,
    timestamps: false
});

이렇게 정의된 model에 대해서 sync를 하면 어떻게 될까? postgres에서 Database공간의 table의 리스트를 보는 명령어는 \dt 이다. sync를 하기전 database의 상황을 살펴보자

sequlize=> \dt
No relations found.

현재 Database공간에는 아무런 테이블도 생성되지 않았다. 그렇다면 다음과 같이 sync 메소드를 호출해보자.

db.Publisher.sync();

콘솔창에서는 로그를 확인할 수있다.


CREATE TABLE IF NOT EXISTS “publisher” 가 맨 윗줄에 보이는가? Publisher model에 대한 sync메소드를 호출시 하는 작업은 publisher테이블을 생성하는 일이다. NOT EXISTS조건이 있기 때문에 sync를 매번 하더라도 table이 없을때만 테이블 생성이 될 것이다. 다시 한번 테이블 리스트를 확인해보자.

 콘솔창 로그2

publisher 테이블이 생성된 것을 확인할 수 있다. 세부적인 테이블정보를 보면 publisher model을 정의했던 내용대로 pub_id는 primary key로 지정되었고 auto increment sequence를 갖게 되었다. 나머지 column들도 정의된대로 테이블이 생성된 것을 확인 할 수 있다. sync와 반대되는 개념의 메소드는 drop이다. drop은 테이블을 drop한다. drop메소드를 실행하면

db.Publisher.drop();

 콘솔창 로그3

drop명령어가 실행되고 publisher테이블이 삭제된 것을 확인할 수 있다. sync에는 몇가지 옵션을 parameter로 넘길 수 있는데 대표적으로 force옵션이 있다.

db.Publisher.sync({force: true});

force를 true로 놓고 sync를 실행하면 publisher테이블은 먼저 drop된 이후에 새로 생성이 된다. force옵션의 경우에는 민감한 작업을 실행하는 만큼 주의를 요하도록 한다. 또한 소스코드내에서 table에 대한 DDL까지 실행하는 것은 위험부담이 있기 때문에 되도록이면 table에 대한 관리는 flyway와 같은 관리모듈을 사용하거나 수동으로 진행하고 sync, drop 그리고 특히 force true 옵션 사용은 자제하도록 한다.

Querying(SELECT)

이제 Sequelize를 통해 SELECT를 사용해보도록 하자.출판사 리스트를 가져오는 쿼리는 다음과 같다.

models.Publisher.findAll().then(function(results) {
    res.json(results);
}).catch(function(err) {
    //TODO: error handling
});

publisher테이블에 있는 내용을 모두 가져오는 쿼리이다. Sequelize의 모든 쿼리 결과는 promise로 리턴 되기 때문에 findAll메소드 역시 promise를 리턴한다. 실제 쿼리 결과는 then의 callback에서 가져올 수 있다. catch문에서는 적절한 error handling을 해주면 된다. 이렇게 API를 호출해서 받아온 출판사리스트를 확인할 수 있다. Sequelize를 통해 가져온 값이다.

 스크린샷

SELECT와 관련있는 model 메소드는 findAll말고도 몇개가 더 있다. 아래 테이블을 참고하자.


이중에서 가장 많이 사용되는것은 findAll, find 이다. 공통적으로 많이 사용되는 옵션에 대해서 알아보자

  findAll([options]) -> Promise.<Array.<Instance>>

1) where : where옵션은 SQL문에서 where에 해당하는 부분을 기술하는 옵션이다.

  • type : object

  • example : SELECT * FROM publisher WHERE pub_id=1 을 sequelize로 표현하면

models.Publisher.findAll({
        where: {pub_id: 1}
}).then(function(result) {
    //TODO: query결과가 담긴 result에 대한 처리 진행
});
  • example : SELECT * FROM publisher WHERE pub_id > 1 AND pub_id < 4
models.Publisher.findAll({
        where: {
            $and: [{pub_id: {$gt: 1}}, {pub_id: {$lt: 4}}]
        }
}).then(function(results) {
        //TODO: query결과가 담긴 result에 대한 처리 진행
});

더 많은 예제는 이곳에서 찾을 수 있다. Sequelize model usage

2) attributes: table의 특정 column만 select할 때 사용

  • type: array | object

  • example : SELECT pub_id, name FROM publisher 를 sequelize로 표현하면

models.Publisher.findAll({
        attributes: ['pub_id', 'name']
}).then(function(results) {});
  • example : SELECT pub_id, name FROM publisher는 다음과 같은 방식으로도 표현
models.Publisher.findAll({
        attributes: {exclude: ['established_date']}
}).then(function(results) {});
// exclude는 table의 column중에서 제외시킬 리스트를 넣고 include에는 포함시킬 리스트를 넣는다.

3) order: SQL문의 order by에 해당하는 부분을 기술하는 옵션이다.

  • type: string | array

  • example : SELECT * FROM publisher ORDER BY pub_id DESC 는 다음과 같다.

models.Publisher.findAll({
        order: 'pub_id DESC'
}).then(function(results){});
// order: 'pub_id DESC’ 는 order: [['pub_id', 'DESC']] 로도 사용할 수 있다.

4) limit, offset: SQL문의 limit, offset에 해당하는 부분을 기술하는 옵션

  • type : Number

  • example : SELECT * FROM publisher LIMIT 1 OFFSET 1은 다음과 같다.

models.Publisher.findAll({
    offset: 1,
    limit: 1
}).then(function(result) {});

5) include: Eager loading, Relation과 관련된 옵션으로 다음 챕터에서 알아보도록 하자.

6) transaction: 어떤 하나의 트랜잭션 안에서 동작하도록 하는 옵션. 다다음 챕터에서 알아보도록하자.

findAll, find에 대해 더 알아보고 싶다면 이 곳을 참고하자 Sequelize-model-findAll

Querying(INSERT)

예제 Publisher 메뉴에 가면 이런 화면을 볼 수 있을 것이다.

 인서트스샷1

우측에는 미리 넣어놓은 5개의 출판사 정보가 있고 ‘Webframeworks.kr’ 의 이름을 가진 새 출판사를 등록해보자. Register버튼을 누르면 등록 API를 call하고 서버에서는 이와 같은 쿼리를 실행한다.

models.Publisher.create({name: options.name}).then(function(result) {
    res.json(result);
}).catch(function(err) {
    //TODO: error handling
});

options.name에는 화면에서 적었던 Webframeworks.kr 문자열이 들어있다. Sequelize model의 create메소드는 SQL의 INSERT INTO와 같은 역할을 한다. 메소드의 형태는 다음과 같다.

create(values, [options]) -> Promise.<Instance>

values에는 실제로 insert할 값들에 대한 object가 들어간다. options에는 부가적인 정보가 들어가는데 transaction에 대한 정보가 대표적이다. create메소드 역시 promise를 리턴하며 성공적으로 쿼리가 실행되었을 경우에는 insert된 row정보를 얻을 수 있다. API호출이 성공되면 화면에서 새로 추가된 출판사 정보를 확인할 수 있다.

 인서트스샷3

row여러개를 한꺼번에 insert하려면 bulkCreate 메소드를 이용하자 create와 사용방법은 똑같다. 차이점은 첫번째 파라미터에 value object들에 대한 array가 들어가야 한다는 것이다.

bulkCreate(records, [options]) -> Promise.<Array.<Instance>>

create메소드에 대해서 더 알아보고 싶다면 이곳을 참조하자 Sequelize-model-create bulkCreate메소드에 대해서 더 알아보고 싶다면 이곳을 참조하자 Sequelize-model-bulkCreate

Querying(UPDATE)

등록한 출판사의 이름이 잘못등록되어 수정이 필요하다면? Webframeworks.kr의 출판사 이름을 Webframeworks로 바꿔보자. 아래 예제 그림처럼 변경할 출판사 이름을 적고 Update버튼을 누르면 변경 API를 call하고 서버에서는 다음과 같은 쿼리를 실행한다.

 업데이트스샷1

models.Publisher.update({name: newName},
 {where: {pub_id: pub_id}, returning: true}).then(function(result) {
      res.json(result[1][0]);
 }).catch(function(err) {
      //TODO: error handling
 });

SQL문으로는 UPDATE publisher SET name='Webframeworks' WHERE pub_id=6 RETURNING * 과 같다. update메소드는 조건에 맞는 복수개의 row에 대해서 update를 실행한다. 메소드의 형태는 이렇다.

update(value, options) -> Promise.<Array.<affectedCount, affectedRows>>

values에는 update해야할 value들의 object가 들어간다. options에 사용되는 것중 대표적으로는 transaction관련 옵션이 있고 postgres에서만 사용할 수 있는 returning옵션이 있다 returning 옵션이 true이면 update결과 후 row의 정보가 리턴된다. 결과로는 array타입이 리턴되는데 첫번째 인덱스에서는 update된 row의 갯수를 두번째 인덱스에서는 update된 row들의 정보를 얻을 수 있다. API호출이 성공되면 다음과 같이 값이 정상적으로 update된것을 확인할 수 있다.

 업데이트스샷2

update메소드에 대해 더 알아보고 싶다면 이곳을 참조하자 Sequelize-model-update

Querying(DELETE)

위에 예제 스크린샷들을 보면 맨 오른쪽에 있는 휴지통 빨간버튼이 보일 것이다. 이 버튼은 해당 출판사의 정보를 삭제하는 버튼이다. Webframeworks로 등록된 출판사를 삭제해보자. 버튼을 누르면 삭제 API를 call하고 서버에서는 다음과 같은 쿼리가 실행된다.

models.Publisher.destroy({where: {pub_id: pub_id}}).then(function(result) {
    res.json({});
}).catch(function(err) {
    //TODO: error handling
});

SQL문으로는 DELETE FROM publisher where pub_id = 6; 과 같다 destroy메소드는 SQL에서 DELETE와 같은 역할을 하며 조건에 맞는 복수개의 row를 삭제한다. 메소드의 형태는 이렇다.

destory(options) -> promise<integer>

options에 필수적으로 빼놓지 말고 넣어야 하는 사항은 where조건이다. where조건을 넣지 않고 destroy메소드를 실행하면 테이블에 있는 모든 row가 삭제되므로 꼭꼭 조심하도록 하자. 그외에는 transaction 옵션등이 들어간다. delete메소드에 대해 더 알아보고 싶다면 이곳을 참조하자. Sequelize-model-destroy

이번챕터에는 정의된 model에 대한 sync작업과 간단하게 CRUD작업을 할 수 있는 예제를 통해 Sequelize 사용법을 알아보았다. find, findAll의 경우에는 option을 어떻게 주느냐에 따라 복잡한 쿼리에 대응하는 작업도 할 수 있는 만큼 많이 사용해보길 바란다. 다음 챕터에서는 Sequelize로 테이블의 relation을 만들어주고 join을 포함한 작업 및 raw query의 사용법등을 알아보도록 한다.




Associations

예제의 다음 기능은 출판사 별로 등록된 책을 보여주고 새로운 서적을 books테이블에 등록하는 일이다. publisher테이블에 이어 books테이블도 만들어 보자. SQL문으로는 다음과 같다.

CREATE TABLE IF NOT EXISTS books (
    book_id SERIAL PRIMARY KEY,
    pub_id INTEGER REFERENCES publisher NOT NULL,
    title VARCHAR(64) NOT NULL,
    author VARCHAR(16) NOT NULL,
    stock SMALLINT NOT NULL DEFAULT 1,
    register_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

books테이블의 pubid는 책의 출판사ID이고 publisher테이블의 pubid를 foreign key로 갖는다. Sequelize의 model define으로 표현하면 이렇게 될 것이다.

sequelize.define('Books', {
    book_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
    pub_id: {type: DataTypes.INTEGER, allowNull: false, references: {model: models.Publisher, key: 'pub_id'}},
    title: {type: DataTypes.STRING(64), allowNull: false},
    author: {type: DataTypes.STRING(16), allowNull: false},
    stock: {type: DataTypes.INTEGER, defaultValue: 1},
    register_date: {type: DataTypes.DATE, defaultValue: DataTypes.NOW}
}, {
    classMethods: {},
    tableName: 'books',
    freezeTableName: true,
    underscored: true,
    timestamps: false
});

여기서 publisher 테이블과 다른 점은 books테이블의 column하나가 reference라는 것이다. 출판사는 여러개의 책을 출판하므로 1:N의 관계가 성립된다. Sequelize에서는 model간의 관계를 정의하는 4가지의 association 메소드가 있다. (hasMany, hasOne, belongsTo, belongsToMany) 이 중에서 이번 예제에서 필요한 hasMany 메소드부터 하나씩 살펴보도록하자. association 설정은 다음과 같이 한다.

db.Publisher.hasMany(db.Books, {foreignKey: 'pub_id'});

Publisher model과 Books model이 1:N으로 관계되어있다는 뜻이다. hasMany는 1:N관계를 맺을 때 사용하는 메소드이다. association관련 메소드의 option항목은 대표적으로 foreignKey, as가 있다. foreignKey에는 foreignKey로 사용되는 column의 name이 들어간다





ref : http://webframeworks.kr/tutorials/expressjs/expressjs_orm_one/

ref : http://webframeworks.kr/tutorials/expressjs/expressjs_orm_two/#tocAnchor-1-1

ref : http://webframeworks.kr/tutorials/expressjs/expressjs_orm_three


반응형
반응형

JWT 의 생김새

JWT 는 . 을 구분자로 3가지의 문자열로 되어있습니다. 구조는 다음과 같이 이루어져있습니다:

자, 그럼 이렇게 3가지 부분으로 나뉘어져 있습니다



우선 암호화한다음 복호화된 결과를 보고 넘어가겠습니다


왼쪽은 암호화된 값, 오른쪽은 복호화된 값입니다






Header, payload 부분은 아래 소스코드 처럼 암호화하면 되고 마지막 signature 부분은


(encodedHeader + '.' + encodedPayload) 이 내용을 다시 암호화 하면 되고


최종 jwt 는 이 3개를 '.' 으로 연결하여 최종 jwt 를 만들 수 있습니다

하단 소스코드에서 확인 할 수 있고 복호화 과정은  https://jwt.io/


사이트에서 암호화된 키를 넣으면 복호화 되는 것을 볼 수 있는데 


Invalid Signature 이렇게 드면 제대로 복호화가 안됐다는 것이고

 Signature Verified 이렇게 드면 제대로 복호화가 정상으로 됐다는 뜻입니다


// encode to base64
const encodedHeader = new Buffer(JSON.stringify({암호화할 내용들}))
    .toString('base64')
    .replace('=', '');






헤더 (Header)

Header 는 두가지의 정보를 지니고 있습니다.

typ: 토큰의 타입을 지정합니다. 바로 JWT 이죠.

alg: 해싱 알고리즘을 지정합니다.  해싱 알고리즘으로는 보통 HMAC SHA256 혹은 RSA 가 사용되며, 이 알고리즘은, 토큰을 검증 할 때 사용되는 signature 부분에서 사용됩니다.


(예제에서는 HMAC SHA256 이 해싱 알고리즘으로 사용됩니다)

이 정보를 base64 로 인코딩을 하면 아래 소스처러 됩니다

정보 (payload)

Payload 부분에는 토큰에 담을 정보가 들어있습니다. 여기에 담는 정보의 한 ‘조각’ 을 클레임(claim) 이라고 부르고, 이는 name / value 의 한 쌍으로 이뤄져있습니다. 토큰에는 여러개의 클레임 들을 넣을 수 있습니다.

클레임 의 종류는 다음과 같이 크게 세 분류로 나뉘어져있습니다:

등록된 (registered) 클레임,
공개 (public) 클레임,
비공개 (private) 클레임

그럼, 하나 하나 알아볼까요?

#1 등록된 (registered) 클레임

등록된 클레임들은 서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기위하여 이름이 이미 정해진 클레임들입니다. 등록된 클레임의 사용은 모두 선택적 (optional)이며, 이에 포함된 클레임 이름들은 다음과 같습니다:

  • iss: 토큰 발급자 (issuer)
  • sub: 토큰 제목 (subject)
  • aud: 토큰 대상자 (audience)
  • exp: 토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야합니다.
  • nbf: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.
  • iat: 토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있습니다.
  • jti: JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.

#2 공개 (public) 클레임

공개 클레임들은 충돌이 방지된 (collision-resistant) 이름을 가지고 있어야 합니다. 충돌을 방지하기 위해서는, 클레임 이름을 URI 형식으로 짓습니다.

{
    "https://velopert.com/jwt_claims/is_admin": true
}

#3 비공개 (private) 클레임

등록된 클레임도아니고, 공개된 클레임들도 아닙니다. 양 측간에 (보통 클라이언트 <->서버) 협의하에 사용되는 클레임 이름들입니다. 공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할때에 유의해야합니다.

{
    "username": "velopert"
}







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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 
/*
 주의: base64로 인코딩을 할 때 dA== 처럼 뒤에 = 문자가 한두개 붙을 때가 있습니다. 
 이 문자는 base64 인코딩의 padding 문자라고 부릅니다.
JWT 토큰은 가끔 URL 의 파라미터로 전달 될 때도 있는데요, 이 = 문자는, url-safe 하지 
않으므로, 제거되어야 합니다. 패딩이 한개 생길 때도 있고, 두개 생길 때도 있는데, 
전부 지워(제거해줘도 디코딩 할 때 전혀 문제가 되지 않습니다)
 */
 
const header = { //헤더 정보
    "typ": "JWT",
    "alg": "HS256"
};
 
 
 
// encode to base64
const encodedHeader = new Buffer(JSON.stringify(header))
    .toString('base64')
    .replace('=', '');
 
console.log("");
console.log("encodedHeader:\n" + encodedHeader);
 
 
 
const payload = {
    "iss": "velopert.com",
    "exp": "1485270000000",
    "https://velopert.com/jwt_claims/is_admin": true,
    "userId": "11028373727102",
    "username": "velopert"
};
 
// encode to base64
const encodedPayload = new Buffer(JSON.stringify(payload))
    .toString('base64')
    .replace('=', '');
 
console.log("");
console.log('encodedPayload:\n' + encodedPayload);
 
 
//JSON Web Token 의 마지막 부분은 바로 서명(signature) 입니다. 
//이 서명은 헤더의 인코딩값과, 정보의 인코딩값을 합친후 
//주어진 비밀키로 해쉬를 하여 생성합니다.
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', 'secret')
    .update(encodedHeader + '.' + encodedPayload)       //'.' 으로 합쳐줘야함
    .digest('base64')
    .replace('=', '');
 
console.log("");
console.log('signature:\n' + signature);
 
const token = encodedHeader + '.' + encodedPayload + '.' + signature;
 
 
console.log("");
console.log('jwt token:\n' + token);

cs



ref : https://velopert.com/2389


반응형
반응형

MYSQL 외래키(Foreign key) 지정(RESTRICT, CASCADE, NO ACTION, SET NULL)



MYSQL 외래키(Foreign key) 지정 시 

on delete rule(삭제 시), on update rule(변경 시) 옵션 지정


RESTRICT, CASCADE, NO ACTION, SET NULL


1. RESTRICT : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 변경/삭제가 취소됩니다.(제한)


2. CASCADE : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 함께 변경/삭제됩니다.


3. NO ACTION : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 변경/삭제할 개체만 변경/삭제되고 참조하고 있는 개체는 변동이 없습니다.


4. SET NULL : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 참조하고 있는 값은 NULL로 세팅됩니다.



ref : http://h5bak.tistory.com/125

반응형
반응형

Example

Create a server that listens on port 8080 of your computer.

When port 8080 get accessed, write "Hello World!" back as a response:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type''text/plain'});
  res.write('Hello World!');
  res.end();
}).listen(8080);


Definition and Usage

The server.listen() method creates a listener on the specified port or path.


Syntax

server.listen(porthostnamebacklogcallback);

Parameter Values

ParameterDescription
portOptional. Specifies the port we want to listen to
hostnameOptional. Specifies the IP address we want to listen to
backlogOptional. Specifies the max length of the queue of pending connections. Default 511
callbackOptional. Specifies a function to be executed when the listener has been added

Technical Details

Return Value:None
Node.js Version:0.1.90


ref : https://www.w3schools.com/nodejs/met_server_listen.asp

반응형
반응형

미들웨어 사용

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크이며, Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시됩니다.

미들웨어 함수는 다음과 같은 태스크를 수행할 수 있습니다.

  • 모든 코드를 실행.
  • 요청 및 응답 오브젝트에 대한 변경을 실행.
  • 요청-응답 주기를 종료.
  • 스택 내의 그 다음 미들웨어 함수를 호출.

현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다.

Express 애플리케이션은 다음과 같은 유형의 미들웨어를 사용할 수 있습니다.

애플리케이션 레벨 및 라우터 레벨 미들웨어는 선택적인 마운트 경로를 통해 로드할 수 있습니다. 일련의 미들웨어 함수를 함께 로드할 수도 있으며, 이를 통해 하나의 마운트 위치에 미들웨어 시스템의 하위 스택을 작성할 수 있습니다.

애플리케이션 레벨 미들웨어

app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드하십시오. 이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메소드입니다.

다음 예에는 마운트 경로가 없는 미들웨어 함수가 표시되어 있습니다. 이 함수는 앱이 요청을 수신할 때마다 실행됩니다.


var app = express();

app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

다음 예에는 /user/:id 경로에 마운트되는 미들웨어 함수가 표시되어 있습니다. 이 함수는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대해 실행됩니다.


app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

다음 예에는 라우트 및 해당 라우트의 핸들러 함수(미들웨어 시스템)이 표시되어 있습니다. 이 함수는 /user/:id 경로에 대한 GET 요청을 처리합니다.


app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

아래에는 하나의 마운트 경로를 통해 일련의 미들웨어 함수를 하나의 마운트 위치에 로드하는 예가 표시되어 있습니다. 이 예는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대한 요청 정보를 인쇄하는 미들웨어 하위 스택을 나타냅니다.


app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

라우트 핸들러를 이용하면 하나의 경로에 대해 여러 라우트를 정의할 수 있습니다. 아래의 예에서는 /user/:id 경로에 대한 GET 요청에 대해 2개의 라우트를 정의합니다. 두 번째 라우트는 어떠한 문제도 발생키지 않지만, 첫 번째 라우트가 요청-응답 주기를 종료시키므로 두 번째 라우트는 절대로 호출되지 않습니다.

다음 예에는 /user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.


app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

라우터 미들웨어 스택의 나머지 미들웨어 함수들을 건너뛰려면 next('route')를 호출하여 제어를 그 다음 라우트로 전달하십시오. 참고next('route')는 app.METHOD() 또는 router.METHOD() 함수를 이용해 로드된 미들웨어 함수에서만 작동합니다.

다음 예에는 /user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.


app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id == 0) next('route');
  // otherwise pass the control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special');
});

라우터 레벨 미들웨어

라우터 레벨 미들웨어는 express.Router() 인스턴스에 바인드된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일한 방식으로 작동합니다.


var router = express.Router();

router.use() 및 router.METHOD() 함수를 사용하여 라우터 레벨 미들웨어를 로드하십시오.

다음 예의 코드는 위에서 애플리케이션 레벨 미들웨어에 대해 표시된 미들웨어 시스템을 라우터 레벨 미들웨어를 사용하여 복제합니다.


var app = express();
var router = express.Router();

// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next router
  if (req.params.id == 0) next('route');
  // otherwise pass control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// mount the router on the app
app.use('/', router);

오류 처리 미들웨어

오류 처리 미들웨어에는 항상 4개의 인수가 필요합니다. 어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개의 인수를 제공해야 합니다. next 오브젝트를 사용할 필요는 없지만, 시그니처를 유지하기 위해 해당 오브젝트를 지정해야 합니다. 그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류 처리에 실패하게 됩니다.

다른 미들웨어 함수와 동일반 방법으로 다음과 같이 오류 처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 구체적으로 말하면 (err, req, res, next) 시그니처를 갖는다는 점이 다릅니다.


app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

오류 처리 미들웨어에 대한 상세 정보는 오류 처리를 참조하십시오.

기본 제공 미들웨어

4.x 버전 이후의 Express는 더 이상 Connect에 종속되지 않습니다. express.static의 실행을 제외하면, 이전에 Express와 함께 포함되었던 모든 미들웨어 함수는 이제 별도의 모듈에 포함되어 있습니다. 미들웨어 함수 목록을 확인하십시오.

express.static(root, [options])

Express의 유일한 기본 제공 미들웨어 함수는 express.static입니다. 이 함수는 serve-static을 기반으로 하며, Express 애플리케이션의 정적 자산을 제공하는 역할을 합니다.

root 인수는 정적 자산의 제공을 시작하는 위치가 되는 루트 디렉토리를 지정합니다.

선택사항인 options 오브젝트는 다음과 같은 특성을 가질 수 있습니다.

특성설명유형기본값
dotfilesdotfile을 제공하기 위한 옵션입니다. 사용 가능한 값은 “allow”, “deny” 및 “ignore”입니다.문자열“ignore”
etagetag의 생성을 사용 또는 사용 안함으로 설정합니다.부울true
extensions파일 확장자 폴백을 설정합니다.배열[]
index디렉토리 인덱스 파일을 전송합니다. 디렉토리 인덱스 작성을 사용하지 않으려면 false를 설정하십시오.혼합“index.html”
lastModifiedOS에서 해당 파일이 마지막으로 수정된 날짜를 Last-Modified 헤더에 설정합니다. 사용 가능한 값은 true 또는 false입니다.부울true
maxAge밀리초 또는 ms 형식의 문자열로 Cache-Control 헤더의 max-age 특성을 설정합니다.숫자0
redirect경로 이름이 디렉토리인 경우 후미부의 “/”로 경로를 재지정합니다.부울true
setHeaders파일을 제공하도록 HTTP 헤더를 설정하기 위한 함수입니다.함수 

아래에는 상세한 옵션 오브젝트와 함께 express.static 미들웨어 함수를 사용하는 예가 표시되어 있습니다.


var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));

다음과 같이, 하나의 앱은 2개 이상의 정적 디렉토리를 가질 수 있습니다.


app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

serve-static 함수 및 그 옵션에 대한 자세한 정보는 serve-static 문서를 참조하십시오.

써드파티 미들웨어

Express 앱에 기능을 추가하려면 써드파티 미들웨어를 사용하십시오.

필요한 기능을 위한 Node.js 모듈을 설치한 후, 애플리케이션 레벨 또는 라우터 레벨에서 해당 모듈을 앱에 로드하십시오.

다음 예는 쿠키 구문 분석 미들웨어 함수인 cookie-parser의 설치 및 로드를 나타냅니다.


$ npm install cookie-parser


var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());

Express와 함께 일반적으로 사용되고 있는 써드파티 미들웨어 함수의 일부 목록을 확인하려면 써드파티 미들웨어를 참조하십시오.

ref :  http://expressjs.com/ko/guide/using-middleware.html#middleware.application


반응형
반응형


먼저 fs는 file system의 약자입니다.  api 문서 링크 : http://nodejs.org/api/fs.html

상당히 많은 함수를 포함하고 있는 파일시스테 모듈이지만 모듈의 반절은 권한관련입니다

(node.js 의 개발자가 지독한 유닉스 개발자로 알려져 있습니다. 디버깅모듈이 콘솔로 된것만으로도 알수 있겠지요?)

많이 쓰는 모듈 몇가지 예제입니다.


1. 파일 확인

var fs = require('fs');

fs.exists('test1.txt', function (exists) {
  console.log(exists ? "it's there" : "no exists!");
});

fs.exists() 파일이 존재 하는지 확인합니다. 예제의 경우 test1.txt 파일이 존재 하는지 확인합니다. 



2. 파일 생성

var fs = require('fs');

var file = 'test1.txt';
fs.open(file,'w',function(err,fd){
	if (err) throw err;
 	console.log('file open complete');
});

fs.open() 해당 파일을 엽니다. 옵션 'w' 의 경우 해당 파일이 없다면 생성합니다. 

해당 옵션에 따라 파일의 접근이 달라집니다. 


  파일을 읽기로 열며 해당 파일이 없다면 에러발생

 r+

  읽기/쓰기 상태로 파일을 열며 파일이 없다면 에러 발생 

 w

  쓰기로 파일을 열며 존재 하지 않으면 생성. 파일이 존재하면 내용을 지우고 처음부터 씀.

 w+

  읽기/쓰기로 열며  존재 하지 않으면 생성. 파일이 존재하면 내용을 지우고 처음부터 씀.

 a

 추가 쓰기로 열며 파일이 존재하지 않으면 만듬. 

 a+

 추가 읽기/쓰기로 열며 파일이 존재 하지 않으면 만듬. 



3. 파일 이름 변경

var fs = require('fs');

fs.rename('test1.txt', 'text2.txt', function (err) {
  if (err) throw err;
  console.log('renamed complete');
});

fs.rename() 해당파일의 이름을 변경합니다. 예제는 test1.txt -> test2.txt  로 변경합니다. 

해당 파일이 없거나 권한이 없다면 에러가 발생합니다. 또한 변경하려는 이름의 같은 이름의 파일이 존재하면 에러가 발생합니다.


4. 파일 삭제

var fs = require('fs');
fs.unlink('text2.txt', function (err) {
  if (err) throw err;
  console.log('successfully deleted text2.txt');
});

fs.unlink() 파일을 삭제 합니다. 예제의 경우 test2.txt 파일을 삭제 합니다. 

해당 파이 없거나 권한이 없다면 에러가 발생합니다. 



5.파일 읽기

var fs = require('fs');

fs.readFile('test1.txt', 'utf8', function(err, data) {
  console.log(data);
});

fs.readFile() 해당 파일을 읽습니다. 예제의 경우 test1.txt 파일을 읽고 콘솔로 출력합니다.



6. 파일 쓰기

var fs = require('fs');

var data = 'file system example!!';
fs.writeFile('text1.txt', data, 'utf8', function(error){
	console.log('write end')
});

fs.writeFile() 해당파일에 내용을 씁니다. 예제의 경우 test1.txt파일에 data의 내용을 씁니다. 

만일 파일이 존재 하지 않으면 파일을 생성후 내용을 씁니다. 파일의 내용을 이어서 쓰진 않습니다.


7.파일 이어서 쓰기

var fs = require('fs');

fs.appendFile('test1.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

fs.appendFile() 해당 파일에 내용을 이어서 씁니다. 예제의 경우 test1.txt 에 'data to append' 를 이어서 씁니다. 파일이 없을경우 새로 생성하여 씁니다. 



대부분의 함수가 파일이 없을 경우 에러를 발생시키므로 먼저 파일을 있는지 확인하거나 파일을 open()를 통하여 쓰기 모드로 연상태에서 해당 함수들을 실행 시킵니다. 다음은 파일 읽기를 할시 파일이 업다면 생성하고 끝내는 예제 입니다. 

var fs = require('fs');
var path = 'test1.txt';
fs.open(path,'a+',function(err,fd){
	if(err) throw err;
	if(fd == '9'){
		console.log('file create.');
	}else{
		fs.readFile(path, 'utf8', function(err, data) {
		  console.log(data);
		});
	}
});




ref : http://uiandwe.tistory.com/933

반응형

'서버(Server) > Server&Nodejs&DB' 카테고리의 다른 글

Node.js server.listen() Method  (0) 2018.07.23
라우터와 미들웨어 use  (0) 2018.07.23
PM2_프로세스 관리 도구  (0) 2018.07.09
파일(모듈) 분리하기  (0) 2018.06.29
쿠키 VS 세션  (0) 2018.06.26
반응형

PM2

  • Node.js 애플리케이션용 프로덕션 프로세스 관리자

  • 로드 밸런서 기본 제공

  • 앱을 항상 작동 상태로 유지

  • 시스템 가동 중단 없이 앱을 다시 로드

    • MAX MEMORY RESTART 기능을 제공하는데 커맨드라인/JSON/코드(JS) 등으로 설정 가능하다.

  • 일반적인 시스템 관리 태스크를 쉽게 처리 가능

  • 애플리케이션 로깅, 모니터링 및 클러스터링을 관리

    • 클러스터 모드의 경우, 여러개의 프로세스를 생성해놓고, 이를 PM2에서 자동으로 로드밸런싱으로 지원해주기 때문에, 앞단 웹 서버(Nginx, Apache Server 등)에서 프록시 설정만 간단히 해줘도 된다.

설치

[sudo] npm install pm2 -g

기본 사용

앱을 시작할때 즉시 백그라운드로 보내진다.

$ pm2 start app.js

메뉴얼을 읽으면서 가장 괜찮다고 생각한 부분이 바로 다른 스크립트 언어도 같이 지원하는 부분이었다.

$ pm2 start echo.pl --interpreter=perl
 
$ pm2 start echo.coffee
$ pm2 start echo.php
$ pm2 start echo.py
$ pm2 start echo.sh
$ pm2 start echo.rb

어플리케이션 설정 파일 사용

JSON, YML 포맷으로 만들어진 파일을 일종의 시작 프로파일로 사용할 수 있다.

$ pm2 start start.yml(start.json)

start.yml

apps:
  script   : app.js
    instances: 4
    exec_mode: cluster
  script : worker.js
    watch  : true
    env    :
      NODE_ENV: development
    env_production:
      NODE_ENV: production

start.json

{
      "apps" : [
            {
            "name"       : "bash-worker",
            "script"     : "./a-bash-script",
            "exec_interpreter": "bash",
            "exec_mode"  : "fork_mode"
            },
            {
            "name"       : "ruby-worker",
            "script"     : "./some-ruby-script",
            "exec_interpreter": "ruby",
            "exec_mode"  : "fork_mode"
            }
      ]
}

CheatSheat

# Fork mode 
$ pm2 start app.js --name my-api # 프로세스 네임 지정 
 
# Cluster mode 
$ pm2 start app.js -i 0     # 가용한 CPU 갯수만큼 클러스터 확장 
$ pm2 start app.js -i max   # 위 설정과 동일하지만 곧 Deprecated 예정? 
 
# Listing 
$ pm2 list               # 모든 프로세스 리스트/상태 확인 
$ pm2 jlist              # 모든 프로세스 리스트/상태 확인(JSON) 
$ pm2 prettylist         # 모든 프로세스 리스트/상태 확인(Beatiful JSON) 
 
$ pm2 describe 0         # 특정 프로세스 정보 확인 
$ pm2 monit              # 모든 프로세스 모니터링 
 
# Logs 
 
$ pm2 logs [--raw]       # Display all processes logs in streaming 
$ pm2 flush              # Empty all log file 
$ pm2 reloadLogs         # Reload all logs 
 
# Actions 
 
$ pm2 stop all           # Stop all processes 
$ pm2 restart all        # Restart all processes 
 
$ pm2 reload all         # Will 0s downtime reload (for NETWORKED apps) 
$ pm2 gracefulReload all # Send exit message then reload (for networked apps) 
 
$ pm2 stop 0             # Stop specific process id 
$ pm2 restart 0          # Restart specific process id 
 
$ pm2 delete 0           # Will remove process from pm2 list 
$ pm2 delete all         # Will remove all processes from pm2 list 
 
# Misc 
 
$ pm2 reset <process>    # Reset meta data (restarted time...) 
$ pm2 updatePM2          # Update in memory pm2 
$ pm2 ping               # Ensure pm2 daemon has been launched 
$ pm2 sendSignal SIGUSR2 my-app # Send system signal to script 
$ pm2 start app.js --no-daemon
$ pm2 start app.js --no-vizion
$ pm2 start app.js --no-autorestart
 

참고





ref : http://stylishc.tistory.com/121

반응형
반응형
파일(모듈) 분리하기





exports를 이용하여 모듈화 하는 방법

Node 개발시 어플리케이션 소스코드를 분석 및 관리를 쉽게 하기 위해 역할별로 관련된 함수들을 파일 단위로 분리하여 사용하는데 이때 이 파일들을 모듈이라 합니다.
CommonJS 표준에 따라 다음과 같이 모듈을 이용할 수 있습니다.
함수를 모듈로 분리하고 등록해주는 방법으로 Node 어디서나 사용 가능한 전역객체인 exports에 개발한 함수(Function)를 할당합니다.
그리고 분리된 모듈을 불러오는 방법으로는 마찬가지로 전역 함수인 require() 함수를 이용합니다.

두 파일이 상대경로상 같은 디렉터리 안에 위치하는 경우 다음과 같이 모듈로 등록하고 불러올 수 있습니다.

calcModule.js
exports.add = function(arg1 , arg2){
    return arg1 + arg2;
}

main.js

var calc = require('./calcModule');
console.log(calc.add(1,3)); //4


require() 함수에 js 확장자를 뺀 파일의 상대경로를 인자로 넘겨주면 해당 모듈이 경로상에 있는지 확인하여 불러옵니다.
만약 해당 경로에 해당하는 파일이 없다면 폴더명으로 인식하여 찾아 폴더 안에 있는 파일들을 불러옵니다.
또한 해당 모듈 안에서도 다른 모듈을 불러올 수 있으며, 그 모듈 또한 안에서 또 다른 모듈을 불러올 수 있습니다.
이렇게 모듈이 사용하는 대상 모듈을 의존 모듈이라 하는데 Node에서의 모듈들은 서로 사슬처럼 얽혀 있습니다.

다음의 소스들을 main에서부터 시작해서 유추하면서 따라가보시면 이해가 될 것입니다.
주의할 점이 있다면 모듈을 로딩하는 시점과 로딩한 모듈안의 함수를 실행하는 시점이 다르다는 점입니다.

calcModule.js
console.log('calcModule.js 실행');
exports.add = function(arg1 , arg2){
    console.log('calcModule.js add() 실행');
    return arg1 + arg2;
};
console.log('calcModule.js 종료');


calcModule2.js
console.log('calcModule2.js 실행');
exports.add2 = function(arg1, arg2){
    console.log('calcModule2.js add2() 실행');
    
    var calc = require('./calcModule');
    console.log('calcModule 로딩 완료');
    
    return calc.add(arg1, arg2);
};
console.log('calcModule2.js 종료');

main.js 
console.log('main.js 시작')
var calc2 = require('./calcModule2');
console.log('main.js에서 calc2.add() 호출 ' + calc2.add2(1,3));

결과 
main.js 시작
calcModule2.js 실행
calcModule2.js 종료
calcModule2.js add2() 실행
calcModule.js 실행
calcModule.js 종료
calcModule 로딩 완료
calcModule.js add() 실행
main.js에서 calc2.add() 호출 4


module.exports를 이용하여 모듈화 하는 방법
다음과 같이 module.expots 에 할당하고 require를 통해 불러올 수도 있습니다.
같은 변수와 같은 함수명을 사용했는데 require로 불러 올 때 변수명을 통해 불러오는 것 처럼 보이지만 calc 객체의 참조값을 가져온다는 점에 주의합니다.
때문에 모듈1의 calc 객체와 모듈2의 calc 객체는 서로 다른객체 즉 서로 다른 참조값을 가지므로 main.js의 calc.add와 calc2.add는 서로 다른 객체의 add 메서드임을 확인할 수 있습니다.


calcModule.js
var calc = {};
calc.add = function(arg1 , arg2){
    console.log('cal1');
    return arg1 + arg2;
};
module.exports = calc;

calcModule2.js
var calc = {};
calc.add = function(arg1 , arg2){
    console.log('cal2');
    return arg1 + arg2;
};
module.exports = calc;

main.js 
var calc = require('./calcModule');
var calc2 = require('./calcModule2');
console.log('main.js에서 calc.add() 호출 ' + calc.add(1, 3));
console.log('main.js에서 calc2.add() 호출 ' + calc2.add(1, 3));


결과 
cal1
main.js에서 calc.add() 호출 4
cal2
main.js에서 calc2.add() 호출 4




ref : http://dololak.tistory.com/97


반응형
반응형


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

//쿠키 정보  예제, 쿠키 정보는 브라우저에서 삭제 가능, 즉 서버가 꺼져도 브라우저에 정보가 남아 있게된다
router.route('/count').get(function (req, res) {
    console.log('/count ');
 
    //res 에는 cookie-parser() 를 등록하여 cookie 함수가 생기게됨
    //웹브라우저에 쿠키 정보를 저장하기 위한 값들을 res 에 저장함
    var count = 0;
    if (req.cookies.count) {
        count = parseInt(req.cookies.count) + 1;
    } else {
        count = 0;
    }
 
    res.cookie('count', count);
    ///process/showCookie 로 이동
    res.send('count : ' + count);
});
 
 
 
//session 정보는 브라우저에서 정보가 암호화된 형태로 보여지며 실제 정보는 서버에서 관리한다
//즉 서버를 껐다 키면 해당 정보가 램에서 날라간다
router.route('/countS').get(function (req, res) {
    console.log('/countS ');
    //req.session.count     //session 객체까지가 원래 존재하는 구문이고 뒤에 .count 는 세션에 추가한 변수
    if (req.session.count) {//브라우저에서 cookies 정보 중 connect.sid 가 세션 아이디이다
        req.session.count++;    
    } else {
        req.session.count = 1;
    }
    res.send('count : ' + req.session.count);
});









이미지 출처 : http://croak.tistory.com/96

반응형
반응형


Mysql & nodejs 로 가입, 로그인 처리하기(관계형 데이터베이스 사용)










[Mysql + HeidiSQL + nodejs(express, 각종 모듈) 조합]



소스는 연습을 위한 코드임으로 날림주의!


Nodejs 에서 관계형 데이터 베이스 사용


MySQL, Oracle 등에서 사용하는 구조



데이터베이스  테이블 집합의 형태





데이터베이스로 연결 및 사용하는 방법






MySQL 을 사용하기 위해선 먼저 프로그램이 필요함

https://dev.mysql.com/downloads/


https://dev.mysql.com/downloads/mysql/




heidisql 을 설치(프리)

https://www.heidisql.com/screenshots.php?which=query




 상단에 데이터 탭을 선택하면 추가된 데이터또한 볼 수 있다


heidisql 로 좀 더 간편하게 데이터베이스를 조작할 수 있다



localhost 로 접속하여 기본 데이터 베이스 필드 만들고  DB명 test, 테이블 명 users


DB : test  생성 후 test 에서 오른족 버튼 클릭 새로생성 -> table   로 테이블 생성

필드는 아래처럼





id 를 Primary key 로 설정해주면 중복가입을 막을 수 있다


아래는 id 를 primary 를 적용한 모습

테이블 탭에서 id 를 primary key 로 적용 할 수 있다(데이터 추가는 서버 실행 시키고 난 이후 가입 정보를 입력하여 등록된 정보들이다








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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>login2</title>
</head>
<body>
 
    <h1>login2</h1>
    <form method="post" action="/process/Login">
        <table>
            <tr>
                <td><label>ID : </label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>PW : </label></td>
                <td><input type="text" name="passwords"></td>
 
 
            </tr>
 
            <tr>
                <td><input type="submit" value="Sumit" name=""></td>
                <td><input type="button" value="Join" onclick="location.href='/addUser2.html'"></td>
            </tr>
 
        </table>
 
    </form>
 
</body>
</html>


login2.html










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
41
42
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>Add User</title>
</head>
<body>
 
    <h1>Add User</h1>
    <form method="post" action="/process/addUser">
        <table>
            <tr>
                <td><label>ID : </label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>PW : </label></td>
                <td><input type="text" name="passwords"></td>
            </tr>
 
            <tr>
                <td><label>name : </label></td>
                <td><input type="text" name="name"></td>
            </tr>
 
            <tr>
                <td><label>age : </label></td>
                <td><input type="text" name="age"></td>
            </tr>
 
            <tr>
                <td><input type="submit" value="Sumit" name=""></td>
                <td><input type="button" value="Login" onclick="location.href='/Login2.html'"></td>
            </tr>
 
        </table>
 
    </form>
 
</body>
</html>



addUser2.html








서버코드


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var expressErrorHandler = require('express-error-handler');
 
 
var mySql = require('mysql');
 
//connection 은 한정되어 있어서 풀을 만들어 그 안에서 사용한다
//connection 할때도 비용이 들어감, 만들고 닫고
 
var pool = mySql.createPool({
    connectionLimit: 10,            //접속을 10개 만들고 10개를 재사용
    host: 'localhost',
    user:'root',
    password: '00000', //MySql 설치할때의 비번을 입력하면 됨!!
    database: 'test',
    debug: false
 
});
 
 
 
var app = express();      //express 서버 객체
 
 
app.set('port'3000);
app.use(serveStatic(path.join('public', __dirname, 'public')));
 
 
 
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 , 아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized: true
}));
 
 
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
router.route('/process/addUser').post(
    function (req, res)
    {
        console.log('process/addUser 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        var paramName = req.body.name || req.query.name;
        var paramAge = Number(req.body.age || req.query.age);
        console.log('id:' + paramID + ', paramPW: ' + paramPW + ' ,paramName: ' + paramName + ' ,paramAge: ' + paramAge);
 
        addUser(paramID, paramName, paramAge, paramPW,
            function (err, result) {
                if (err) {
                    console.log('Error!!!');
                    res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                    res.write('<h1>에러발생 - 이미 존재하는  아이디일수 있음</h1>');
                    res.write('<br><a href="/login2.html"> re login </a>');
                    res.end();
                    return;
                }
 
                if (result)
                {
                    console.dir(result);
                    res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                    res.write('<h1>Add Success</h1>');
                    res.write('<br><a href="/login2.html"> re login </a>');
                    res.end();
                }
                else
                {
                    console.log('데이터베이스에 추가 에러');
                    res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                    res.write('<h1> Failed : add user</h1>');
                    res.write('<a href="/login2.html"> re login</a>');
                    res.end();
                }
            }
        );
    }
);
 
 
 
router.route('/process/login').post(
    function (req, res) {
        console.log('process/login 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        authUser( paramID, paramPW,
            function (err, rows)
            {
                if (err) {
                    console.log('Error!!!');
                    res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                    res.write('<h1>에러발생</h1>');
                    res.end();
                    return;
                }
 
                if (rows) {
                    console.dir(rows);
                    res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                    res.write('<h1>Login Success</h1>');
                    res.write('<h1> user </h1>' + rows[0].name);
                    res.write('<br><a href="/login2.html"> re login </a>');
                    res.end();
 
                }
                else {
                    console.log('empty Error!!!');
                    res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                    res.write('<h1>user data not exist</h1>');
                    res.write('<a href="/login2.html"> re login</a>');
                    res.end();
                }
            }
        );
    }
);
 
 
 
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
var addUser = function(id,name,age,passwords,callback)
{
    console.log('addUser 호출');
 
    //pool로 DB접근 함수 호출(mysql 접근)
    //conn 연결된 객체
    pool.getConnection(
        function (err, poolConn)
        {
            if (err)
            {
                if (poolConn) {
                    poolConn.release();        // 사용한후 해제(반납)한다
                }
                callback(err, null);
                return;
            }
            console.log('데이터베이스 연결 스레드 아이디' + poolConn.threadId);
            var data = { id: id, namename, age: age, passwords: passwords };
 
            //users 테이블에 데이터 추가
            var exec = poolConn.query('insert into users set ?', data,
                function (err, result)
                {
                    poolConn.release();
                    console.log('실행된 SQL : ' + exec.sql);
 
                    if (err) {
                        console.log('sql 실행 시 에러 발생');
                        callback(err, null);
                        return;
                    }
 
                    callback(null, result);
                }
            );
        }
    );
}
 
var authUser = function (id, password, callback) {
    console.log('input id :' + id + '  :  pw : ' + password);
 
 
    pool.getConnection(function (err, poolConn) {
        if (err) {
            if (poolConn) {
                poolConn.release();     //pool 반환처리
            }
 
            callback(err, null);
            return;
        }
 
        console.log('데이터베이스 연결 스레드 아이디' + poolConn.threadId);
 
        var tablename = 'users';
        var columns = ['id''name''age'];
 
 
        //id 와 pw 가 같은것을 조회한다
        var exec = poolConn.query("select ?? from ?? where id = ? and passwords=?", [columns, tablename, id, password],
 
            function (err, rows)
            {
                poolConn.release();     //pool 반환처리
                console.log('실행된 ssql : ' + exec.sql);
 
                if (err) {
                    callback(err, null);
                    return;
                }
 
                if (rows.length > 0) {
                    console.log('사용자 찾음');
                    callback(null, rows);
                } else {
                    console.log('사용자 찾지 못함');
                    callback(nullnull);
                }
 
            }
        );
 
    }
);
 
 
};
 
 
 
 
 
 
var errorHandler = expressErrorHandler(
    { static: { '404''./public/404.html' } }              //404 에러 코드가 발생하면 해당 페이지를 보여주는 예외 미들웨어
);
 
app.use(expressErrorHandler.httpError(404));
app.use(expressErrorHandler);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 



반응형
반응형


mongoDB&mongoose 로 패스워드 

암호화하여 가입 및 로그인처리




DataBAseSample0.zip






PW 가 암호화되어 저장된 Document들






실행  flow 

  1. 회원 가입 시도

    1. 정보 입력

    2. submit 버튼으로 정보 전송

    3. mongooese 와 'crypto' 모듈을 사용하여 pw 암호화 후 암호화 하기 위한 salt 값과 과 암호화된 PW DB에 저장

  2. 로그인

    1. 정보 입력

    2. id를 통한 기본 정보를 가져온 후 pw 를 crypto 모듈을 활용하여 가입시 저장해놓은 salt  값과 로그인을 하기 위해 입력한 pw 를 암호한 후

    3. 존재하는 유저인지 비교한다

    4. 존재하는 유저라면 로그인 처리 완료

    5. 그렇지 않은경우 존재하지 않는 유저라 알림








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /**
   * Defines a model or retrieves it.
   * Models defined on the mongoose instance are available to all connection
   *   created by the same mongoose instance. *
   * @param name model name
* .....
   * @param collection (optional, induced from model name)
   * @param skipInit whether to skip initialization (defaults to false)
   */
  export function model<T extends Document>(name: string, schema?: Schema, collection?: string,
    skipInit?: boolean): Model<T>;
  export function model<T extends Document, U extends Model<T>>(
    name: string,
    schema?: Schema,
    collection?: string,
    skipInit?: boolean
  ): U;



//컬렉션과 스키마를 연결시킴

userModel = mongoose.model('users3', userSchema);


static 은 모댈 객체 userModel 에서 사용 가능한 함수를 등록 하는 것이고

method 는 모델 인스턴스 객체에서 사용 할 수 있는 함수를 등록하는 것




Models

Models are fancy constructors compiled from our Schema definitions. Instances of these models represent documents which can be saved and retrieved from our database. All document creation and retrieval from the database is handled by these models.

Compiling your first model

var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema); 

The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural version of your model name. Thus, for the example above, the model Tank is for the tanks collection in the database. The .model() function makes a copy of schema. Make sure that you've added everything you want to schema before calling .model()! 








서버 코드(nodejs)


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var expressErrorHandler = require('express-error-handler');
 
var mongoose = require('mongoose');
 
 
//암호화 모듈
var crypto = require('crypto');
 
 
 
var database;
 
var userSchema;
 
 
var userModel;
 
 
//몽고디비에 연결 ,  보통 웹서버 만든 직후 연결 , DB 먼저 연결 되도 상관 없음
//먼저 db를 가져온다 
function connectDB() {
    //localhost 로컬 호스트
    //:27017  몽고디비 포트
    //local db 생성시 만든 폴더 명
    var databaseURL = 'mongodb://localhost:27017/test';
 
    mongoose.Promise = global.Promise;
    mongoose.connect(databaseURL);
 
    database = mongoose.connection;     //db와 연결 시도
 
    database.on('open',         //db 연결 될때의 이벤트
        function () {
            console.log('data base 연결됨 ' + databaseURL);
 
 
            //몽구스는 스키마를 정의하고 해당 스키마에 해당 하는 데이터를 집어넣는 방식으로 테이블과 유사
            userSchema = mongoose.Schema({
                //id: String,   타입만 세팅하는 것이 아니고 여러가지 속성을 넣고 싶다면 객체로 추가한다
                id: { type: String, required: true, unique: true'default':'' },         //required : 필수로 넣어야 하는 요소, unique 유일해야하는 값
                hashed_passwords: { type: String, required: true'default''' },                //passwords 는 유니크할 필요는 없음으로 뺌
                salt: {type:String, required:true},
 
                name: { type: String, index: 'hashed''default''' },                    //name 을 넣긴 하는데 hash 로 처리
                age: { type: Number'default'-1 },                        //초기 값을 -1 로
                created_at: { type: Date, index: { unique: false }, 'default'Date.now() },
                updated_at: { type: Date, index: { unique: false }, 'default'Date.now() }
            });
            console.log('userSchema 정의함');
 
//virtual 함수는 passwords 가 new userModel(...) 로 생성자에 의해 실행되면서 값이 넘어갈때 실행된다
  //method 에 추가된 함수는 인스턴스에 함수가 추가된다
//그래서 this.makeSalt() 이런 함수 호출이 가능하다
            userSchema.virtual('passwords').set( //여기서 virtual 은 C++ 에서의 virtual 과는 전혀 상관이 없다
                function (passwords)
                {
                    console.log(userModel === this);        //false
                    console.log(userSchema === this);        //false
                    dd1 = this;
                    //이때의 this 는 추가한 데이터 객체의 this 임
                    this.salt = this.makeSalt();
                    this.hashed_passwords = this.encryptPassword(passwords);
                    console.log('this.hashed_passwords 저장됨 : ' + this.hashed_passwords);
                }
            );
 
            //encryptPassword 속성 추가 , //method 에 추가된 함수는 인스턴스에 함수가 추가된다
            userSchema.method('encryptPassword',
                //plainText : plainText : 가상속성 pw 값
                //inSalt 에 따라서 암호화 방식을 다르게함
                function (plainText, inSalt) {
                if (inSalt) {
                    //'sha1'암호화 방식
                    //plainText : 가상속성 pw 값
                    //digest 암호화 처리
                    return crypto.createHmac('sha1', inSalt).update(plainText).digest('hex');
                } else {
                    //'sha1'암호화 방식
                    //document 만들때 virtual 함수에서  salt을 저장한 값을 그대로 가져와서 암호화한다
                    return crypto.createHmac('sha1', this.salt).update(plainText).digest('hex');
                }
                });
 
 
 
            userSchema.method('makeSalt',
                function (plainText, inSalt) {
 
                    console.log(userModel === this);        //false
                    console.log(userSchema === this);        //false
 
                    return Math.round(new Date().valueOf() * Math.random()) + '' ;
                });
 
            userSchema.method('authenticate',
                function (plainText, inSalt, hashed_passwords) {
                    if (inSalt) {
                        //insalt 키와 패스워드를 암호화 하여 데이터베이트 암호화된 pw 와 같은지 비교
                        return this.encryptPassword(plainText, inSalt) === hashed_passwords;
                    } else {
                        return this.encryptPassword(plainText) === hashed_passwords;
                    }
                });
 
 
            //모델 객체에 사용할 수 있는 함수를 추가(등록)한다
            //스키마에 함수를 추가 했지만 모델에서 사용 할 수 있음
            userSchema.static('findByID',
                function (id, callback) {
                    //userModel == this;
                    console.log(userModel == this);
                    return this.find({ id: id }, callback);
                }
            );
 
            /* //객체를 넣을 수도 있음
             userSchema.static.findByID = function (id, callback)
                {
                //this 는모델객체를 의미함
                    return this.find({id:id}, callback);
                }
            );
            */
 
            userSchema.static('findAll',
                function (callback) {
                    return this.find({}, callback);
                }
            );
 
 
 
            //컬렏션과 스키마를 연결시킴
            userModel = mongoose.model('users3', userSchema);
            console.log('userModel 정의함');
        }
    );
 
    database.on('disconnected',         //db 연결 끊길떄
        function () {
            console.log('data base 연결 끊어짐');
        }
    );
 
    database.on('error',         //에러 발생하는 경우
        console.error.bind(console'mongoose 연결 에러')
    );
 
}
 
 
 
 
 
var app = express();      //express 서버 객체
 
 
app.set('port'3000);
app.use(serveStatic(path.join('public', __dirname, 'public')));
 
 
 
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 , 아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized: true
}));
 
 
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
router.route('/process/login').post(
    function (req, res) {
        console.log('process/login 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database) {
            authUser(database, paramID, paramPW,
                function (err, docs) {
                    if (database) {
                        if (err) {
                            console.log('Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                            res.write('<h1>에러발생</h1>');
                            res.end();
                            return;
                        }
 
                        if (docs) {
                            console.dir(docs);
                            res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                            res.write('<h1>Login Success</h1>');
                            res.write('<h1> user </h1>' + docs[0].id + '  :   ' + docs[0].name);
                            res.write('<br><a href="/login.html"> re login </a>');
                            res.end();
 
                        }
                        else {
                            console.log('empty Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                            res.write('<h1>user data not exist</h1>');
                            res.write('<a href="/login.html"> re login</a>');
                            res.end();
                        }
 
                    }
                    else {
                        console.log('DB 연결 안됨');
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>databasae 연결 안됨</h1>');
                        res.end();
                    }
 
 
 
                }
            );
        }
    }
);
 
 
router.route('/process/addUser').post(
 
    function (req, res) {
        console.log('process/addUser 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        var paramName = req.body.name || req.query.name;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database) {
            addUser(database, paramID, paramPW, paramName,
                function (err, result) {
                    if (err) {
                        console.log('Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>에러발생 - 이미 존재하는 아이디일 수 있습니다</h1>');
                        res.write('<br><a href="/login.html"> re login </a>');
                        res.end();
                        return;
                    }
 
                    if (result) {
                        console.dir(result);
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>Add Success</h1>');
                        res.write('<h1> name </h1>' + paramName);
                        res.write('<br><a href="/login.html"> re login </a>');
                        res.end();
                    }
                    else {
                        console.log('추가 안됨 Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>can not add user</h1>');
                        res.write('<a href="/login.html"> re login</a>');
                        res.end();
                    }
 
                }
            );
        }
        else {
            console.log('DB 연결 안됨');
            res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
            res.write('<h1>databasae 연결 안됨</h1>');
            res.end();
        }
 
    }
);
 
 
router.route('/process/listUser').post(
    function (req, res) {
        console.log('process/listuser 호출됨');
        if (database) {
            userModel.findAll(
                function (err, results) {
                    if (err) {
                        console.log('Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>전체 유저 검색 - 에러발생</h1>');
                        res.end();
                        return;
                    }
 
                    if (results) {
                        console.dir(results);
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>User List</h1>');
 
                        res.write('<div><ul>');
                        for (var i = 0; i < results.length++i) {
                            var curID = results[i]._doc.id;
                            var curName = results[i]._doc.name;
                            res.write("<li>#" + i + " ->" + curID + " , " + curName + " </li>");
                        }
                        res.write('</ul></div>');
 
                        res.write('<br><a href="/login.html"> re login </a>');
                        res.end();
                    }
                    else {
                        console.log(' 조회된 사용자 없음');
                        res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
                        res.write('<h1>조회된 사용자 없음</h1>');
                        res.write('<a href="/login.html"> re login</a>');
                        res.end();
                    }
                }
            );
        }
        else {
            console.log('DB 연결 안됨');
            res.writeHead(200, { "Content-Type""text/html;charset=utf-8" });
            res.write('<h1>databasae 연결 안됨</h1>');
            res.end();
        }
    }
);
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
 
var dd1;
 
var authUser = function (db, id, password, callback) {
    console.log('input id :' + id.toString() + '  :  pw : ' + password);
 
 
    userModel.findByID(id,
        function (err, result) {
            if (id === undefined || password === undefined) {
                console.log('id or pw 필수');
                return;
            }
            if (err) {
                callback(err, null);
                return;
            }
 
            console.log('아이디 %s 로 검색 ');
            if (result.length > 0) {
 
                var user = new userModel({ id: id });
                console.log(user === dd1);
                //password : 현재 사용자가 로그인 하기 위해 입력한 암호
                //result[0]._doc.salt 암호화를 위해 만들었던 임의의 값
                //result[0]._doc.hashed_passwords : 암호화 되어 DB에 저장되어 있는 암호화된 PW
                var authenticated = user.authenticate(password, result[0]._doc.salt, result[0]._doc.hashed_passwords);
 
                if (authenticated) {
                    console.log(' 비번 일치 ');
                    callback(null, result);
                }
                else {
                    console.log(' 비번 일치하지 않음 ');
                    callback(nullnull);
                }
            }
            else {
                console.log('아이디 일치하는 사용자 없음');
                callback(nullnull);
            }
 
        }
    );
 
 
};
 
 
 
var addUser = function (db, id, passwords, name, callback) {
    console.log('add User 호출됨' + id + '  , ' + passwords);
 
 
    var user = new userModel({ "id": id, "passwords": passwords, "name"name });
 
    //user 정보를 DB 에 저장
    user.save(
        function (err) {
            if (err) {
                callback(err, null);
                return;
            }
 
            //데이터가 추가됐다면 insertedCount 카운트가 0 보다 큰값이 된다
            console.log('사용자 추가 됨');
            callback(null, user);
        }
    );
 
};
 
 
 
 
var errorHandler = expressErrorHandler(
    { static: { '404''./public/404.html' } }              //404 에러 코드가 발생하면 해당 페이지를 보여주는 예외 미들웨어
);
 
app.use(expressErrorHandler.httpError(404));
app.use(expressErrorHandler);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
        connectDB();        //DB 연결 , DB 연결 먼저해도 상관 없음
    }
);
 
cs
















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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
var mongoose = require('mongoose');
 
var database;
var userSchema;
 
 
var userModel;
 
 
 
//pw 를뺌
function createUserSchema()
{
    userSchema = mongoose.Schema({
        //id: String,   타입만 세팅하는 것이 아니고 여러가지 속성을 넣고 싶다면 객체로 추가한다
        id: { type: String, required: true, unique: true },         //required : 필수로 넣어야 하는 요소, unique 유일해야하는 값
        name: { type: String, index: 'hashed' },                    //name 을 넣긴 하는데 hash 로 처리
        age: { type: Number'default'-1 },                        //초기 값을 -1 로
        created_at: { type: Date, index: { unique: false }, 'default'Date.now() },
        updated_at: { type: Date, index: { unique: false }, 'default'Date.now() }
    });
 
    //info 라는 컬러을 설정하면 set 이 호출됨
    userSchema.virtual('info')
        .set(function (info)
        {
            console.log(userModel === this);        //false
            var splitted = info.split(' ');
            this.id = splitted[0];
            this.name = splitted[1];
            console.log('virtual info 속성 설정됨 ' + this.id + ',' + this.name);
        }
        ).get(function ()
        {
                return this.id + ' ' + this.name ;
            }
    );
 
    //collection 과 스미카를 연결하여 usermodel 로 리턴
    userModel = mongoose.model("users4", userSchema);
    console.log('user model 정의함');
}
 
 
var dddd;
function dotest()
{
    var user = new userModel({ "info""aaaa bbbb" });
 
    user.save(
        function (err) {
            if (err) {
                console.log('에러 발생됨');
                return;
            }
 
            //데이터가 추가됐다면 insertedCount 카운트가 0 보다 큰값이 된다
            console.log('사용자 추가 됨');
        }
 
    );    //insert 가 됨
 
}
 
 
 
function connectDB() {
    //localhost 로컬 호스트
    //:27017  몽고디비 포트
    //local db 생성시 만든 폴더 명
    var databaseURL = 'mongodb://localhost:27017/test';
 
    mongoose.Promise = global.Promise;
    mongoose.connect(databaseURL);
 
    database = mongoose.connection;     //db와 연결 시도
 
    database.on('open',         //db 연결 될때의 이벤트
        function () {
            console.log('data base 연결됨 ' + databaseURL);
 
 
            //몽구스는 스키마를 정의하고 해당 스키마에 해당 하는 데이터를 집어넣는 방식으로 테이블과 유사
            createUserSchema();
 
            console.log('userSchema 정의함');
 
            dotest();
 
 
            //모델 객체에 사용할 수 있는 함수를 추가(등록)한다
            //스키마에 함수를 추가 했지만 모델에서 사용 할 수 있음
            userSchema.static('findByID',
                function (id, callback) {
                    //userModel == this;
                    console.log(userModel == this);         //true , static 으로 추가된 함수는 usermodel 에 추가 됨으로
                    return this.find({ id: id }, callback);
                }
            );
 
            /* //객체를 넣을 수도 있음
             userSchema.static.findByID = function (id, callback)
                {
                //this 는모델객체를 의미함
                    return this.find({id:id}, callback);
                }
            );
            */
 
            userSchema.static('findAll',
                function (callback) {
                    return this.find({}, callback);
                }
            );
 
 
 
            //컬렏션과 스키마를 연결시킴
            userModel = mongoose.model('users2', userSchema);
            console.log('userModel 정의함');
        }
    );
 
    database.on('disconnected',         //db 연결 끊길떄
        function () {
            console.log('data base 연결 끊어짐');
        }
    );
 
    database.on('error',         //에러 발생하는 경우
        console.error.bind(console'mongoose 연결 에러')
    );
 
}
 
connectDB();





일부 part ref : http://mongoosejs.com/docs/models.html

반응형
반응형


Indexes

Indexes support the efficient execution of queries in MongoDB. Without indexes, MongoDB must perform a collection scan, i.e. scan every document in a collection, to select those documents that match the query statement. If an appropriate index exists for a query, MongoDB can use the index to limit the number of documents it must inspect.

Indexes are special data structures [1] that store a small portion of the collection’s data set in an easy to traverse form. The index stores the value of a specific field or set of fields, ordered by the value of the field. The ordering of the index entries supports efficient equality matches and range-based query operations. In addition, MongoDB can return sorted results by using the ordering in the index.

The following diagram illustrates a query that selects and orders the matching documents using an index:


Fundamentally, indexes in MongoDB are similar to indexes in other database systems. MongoDB defines indexes at the collection level and supports indexes on any field or sub-field of the documents in a MongoDB collection.

Default _id Index

MongoDB creates a unique index on the _id field during the creation of a collection. The _id index prevents clients from inserting two documents with the same value for the _id field. You cannot drop this index on the _id field.

NOTE

In sharded clusters, if you do not use the _id field as the shard key, then your application must ensure the uniqueness of the values in the _id field to prevent errors. This is most-often done by using a standard auto-generated ObjectId.

Create an Index

To create an index using the Node.JS driver, use createIndex().

collection.createIndex( { <key and index type specification> }, function(err, result) {
   console.log(result);
   callback(result);
}

The following example creates a single key descending index on the name field:

 collection.createIndex( { name : -1 }, function(err, result) {
   console.log(result);
   callback(result);
}

The createIndex() method only creates an index if an index of the same specification does not already exist.

[1]MongoDB indexes use a B-tree data structure.

Index Types

MongoDB provides a number of different index types to support specific types of data and queries.

Single Field

In addition to the MongoDB-defined _id index, MongoDB supports the creation of user-defined ascending/descending indexes on a single field of a document.

Diagram of an index on the ``score`` field (ascending).

For a single-field index and sort operations, the sort order (i.e. ascending or descending) of the index key does not matter because MongoDB can traverse the index in either direction.

See Single Field Indexes and Sort with a Single Field Index for more information on single-field indexes.

Compound Index

MongoDB also supports user-defined indexes on multiple fields, i.e. compound indexes.

The order of fields listed in a compound index has significance. For instance, if a compound index consists of { userid: 1, score: -1 }, the index sorts first by userid and then, within each userid value, sorts by score.


먼저 유저id로 오름차순 정렬 한다음 score 값으로 내림 차순 정렬한 형태



For compound indexes and sort operations, the sort order (i.e. ascending or descending) of the index keys can determine whether the index can support a sort operation. See Sort Order for more information on the impact of index order on results in compound indexes.

See Compound Indexes and Sort on Multiple Fields for more information on compound indexes.

Multikey Index

MongoDB uses multikey indexes to index the content stored in arrays. If you index a field that holds an array value, MongoDB creates separate index entries for every element of the array. These multikey indexes allow queries to select documents that contain arrays by matching on element or elements of the arrays. MongoDB automatically determines whether to create a multikey index if the indexed field contains an array value; you do not need to explicitly specify the multikey type.

Diagram of a multikey index on the ``addr.zip`` field. The ``addr`` field contains an array of address documents. The address documents contain the ``zip`` field.

See Multikey Indexes and Multikey Index Bounds for more information on multikey indexes.

Geospatial Index

To support efficient queries of geospatial coordinate data, MongoDB provides two special indexes: 2d indexesthat uses planar geometry when returning results and 2dsphere indexes that use spherical geometry to return results.

See 2d Index Internals for a high level introduction to geospatial indexes.

Text Indexes

MongoDB provides a text index type that supports searching for string content in a collection. These text indexes do not store language-specific stop words (e.g. “the”, “a”, “or”) and stem the words in a collection to only store root words.

See Text Indexes for more information on text indexes and search.

Hashed Indexes

To support hash based sharding, MongoDB provides a hashed index type, which indexes the hash of the value of a field. These indexes have a more random distribution of values along their range, but only support equality matches and cannot support range-based queries.

Index Properties

Unique Indexes

The unique property for an index causes MongoDB to reject duplicate values for the indexed field. Other than the unique constraint, unique indexes are functionally interchangeable with other MongoDB indexes.

Partial Indexes

New in version 3.2.

Partial indexes only index the documents in a collection that meet a specified filter expression. By indexing a subset of the documents in a collection, partial indexes have lower storage requirements and reduced performance costs for index creation and maintenance.

Partial indexes offer a superset of the functionality of sparse indexes and should be preferred over sparse indexes.

Sparse Indexes

The sparse property of an index ensures that the index only contain entries for documents that have the indexed field. The index skips documents that do not have the indexed field.

You can combine the sparse index option with the unique index option to reject documents that have duplicate values for a field but ignore documents that do not have the indexed key.

TTL Indexes

TTL indexes are special indexes that MongoDB can use to automatically remove documents from a collection after a certain amount of time. This is ideal for certain types of information like machine generated event data, logs, and session information that only need to persist in a database for a finite amount of time.

See: Expire Data from Collections by Setting TTL for implementation instructions.

Index Use

Indexes can improve the efficiency of read operations. The Analyze Query Performance tutorial provides an example of the execution statistics of a query with and without an index.

For information on how MongoDB chooses an index to use, see query optimizer.

Indexes and Collation

New in version 3.4.

Collation allows users to specify language-specific rules for string comparison, such as rules for lettercase and accent marks.

NOTE

The following examples illustrate indexes and collation in the Mongo Shell.

Refer to your driver documentation for instructions on creating indexes with collation in your specific driver.

To use an index for string comparisons, an operation must also specify the same collation. That is, an index with a collation cannot support an operation that performs string comparisons on the indexed fields if the operation specifies a different collation.

For example, the collection myColl has an index on a string field category with the collation locale "fr".

db.myColl.createIndex( { category: 1 }, { collation: { locale: "fr" } } )

The following query operation, which specifies the same collation as the index, can use the index:

db.myColl.find( { category: "cafe" } ).collation( { locale: "fr" } )

However, the following query operation, which by default uses the “simple” binary collator, cannot use the index:

db.myColl.find( { category: "cafe" } )

For a compound index where the index prefix keys are not strings, arrays, and embedded documents, an operation that specifies a different collation can still use the index to support comparisons on the index prefix keys.

For example, the collection myColl has a compound index on the numeric fields score and price and the string field category; the index is created with the collation locale "fr" for string comparisons:

db.myColl.createIndex(
   { score: 1, price: 1, category: 1 },
   { collation: { locale: "fr" } } )

The following operations, which use "simple" binary collation for string comparisons, can use the index:

db.myColl.find( { score: 5 } ).sort( { price: 1 } )
db.myColl.find( { score: 5, price: { $gt: NumberDecimal( "10" ) } } ).sort( { price: 1 } )

The following operation, which uses "simple" binary collation for string comparisons on the indexed category field, can use the index to fulfill only the score: 5 portion of the query:

db.myColl.find( { score: 5, category: "cafe" } )

For more information on collation, see the collation reference page.

The following indexes only support simple binary comparison and do not support collation:

Covered Queries

When the query criteria and the projection of a query include only the indexed fields, MongoDB returns results directly from the index without scanning any documents or bringing documents into memory. These covered queries can be very efficient.

For more information on covered queries, see Covered Query.

Index Intersection

New in version 2.6.

MongoDB can use the intersection of indexes to fulfill queries. For queries that specify compound query conditions, if one index can fulfill a part of a query condition, and another index can fulfill another part of the query condition, then MongoDB can use the intersection of the two indexes to fulfill the query. Whether the use of a compound index or the use of an index intersection is more efficient depends on the particular query and the system.

For details on index intersection, see Index Intersection.

Restrictions

Certain restrictions apply to indexes, such as the length of the index keys or the number of indexes per collection. See Index Limitations for details.

Additional Considerations

Although indexes can improve query performances, indexes also present some operational considerations. See Operational Considerations for Indexes for more information.

If your collection holds a large amount of data, and your application needs to be able to access the data while building the index, consider building the index in the background, as described in Background Construction.

To build or rebuild indexes for a replica set, see Build Indexes on Replica Sets.

Some drivers may specify indexes, using NumberLong(1) rather than 1 as the specification. This does not have any affect on the resulting index.

Additional Resources



ref : https://docs.mongodb.com/manual/indexes/#id2

반응형
반응형

Databases, documents and collections in MongoDB

Descriptions

In this tutorial, we will walk you through the concepts and key facts of databases, documents, and collection of MongoDB.

Databases

A number of databases can be run on a single MongoDB server. Default database of MongoDB is 'db', which is stored within data folder.

MongoDB can create databases on the fly. It is not required to create a database before you start working with it.

"show dbs" command provides you with a list of all the databases.

show-dbs-command.png

Run 'db' command to refer to the current database object or connection.

db command

To connect to a particular database, run use command.

use command

In the above command, 'student' is the database we want to select.

w3resource MongoDB tutorial has a separate page dedicated to commands related to creation and management of the database.

Database names can be almost any character in the ASCII range. But they can't contain an empty string, a dot (i.e. ".") or " ".

Since it is reserved, "system" can't be used as a database name.

A database name can contain "$".

documents

The document is the unit of storing data in a MongoDB database.

document use JSON (JavaScript Object Notation, is a lightweight, thoroughly explorable format used to interchange data between various applications) style for storing data.

A simple example of a JSON document is as follows :

{ site : "w3resource.com" }

Often, the term "object" is used to refer a document.

Documents are analogous to the records of an RDBMS. Insert, update, and delete operations can be performed on a collection. The following table will help you to understand the concept more easily :

RDBMSMongoDB
TableCollection
ColumnKey
ValueValue
Records / RowsDocument / Object

The following table shows the various datatypes which may be used in MongoDB.

Data TypesDescription
stringMay be an empty string or a combination of characters.
integerDigits.
booleanLogical values True or False.
doubleA type of floating point number.
nullNot zero, not empty.
arrayA list of values.
objectAn entity which can be used in programming. May be a value, variable, function, or data structure.
timestampA 64 bit value referring to a time and unique on a single "mongod" instance. The first 32 bit of this value refers to seconds since the UTC January 1, 1970. And last 32 bits refer to the incrementing ordinal for operations within a given second.
Internationalized StringsUTF-8 for strings.
Object IDsEvery MongoDB object or document must have an Object ID which is unique. This is a BSON(Binary JavaScript Object Notation, which is the binary interpretation of JSON) object id, a 12-byte binary value which has a very rare chance of getting duplicated. This id consists of a 4-byte timestamp (seconds since epoch), a 3-byte machine id, a 2-byte process id, and a 3-byte counter.

Collections

collection may store a number of documents. A collection is analogous to a table of an RDBMS.

collection may store documents those who are not same in structure. This is possible because MongoDB is a Schema-free database. In a relational database like MySQL, a schema defines the organization / structure of data in a database. MongoDB does not require such a set of formula defining structure of data. So, it is quite possible to store documents of varying structures in a collection. Practically, you don't need to define a column and it's datatype unlike in RDBMS, while working with MongoDB.

In the following code, it is shown that two MongoDB documents, belongs to same collection, storing data of different structures.

{"tutorial" : "NoSQL"}
               {"topic_id" : 7}

A collection is created, when the first document is inserted.

Pictorial Presentation : Collections and Documents


Valid collection names

Collection names must begin with letters or an underscore.

A Collection name may contain numbers.

You can't use "$" character within the name of a collection. "$" is reserved.

A Collection name must not exceed 128 characters. It will be nice if you keep it within 80/90 characters.

Using a "." (dot) notation, collections can be organized in named groups. For example, tutorials.php and tutorials.javascript both belong to tutorials. This mechanism is called as collection namespace which is for user primarily. Databases don't have much to do with it.

Following is how to use it programmatically :

db.tutorials.php.findOne()

capped collections

Imagine that you want to log the activities happening with an application. you want to store data in the same order it is inserted. MongoDB offers Capped collections for doing so.

Capped collections are collections which can store data in the same order it is inserted.

It is very fixed size, high-performance and "auto-FIFO age-Out". That is, when the allotted space is fully utilized, newly added objects (documents) will replace the older ones in the same order it is inserted.

Since data is stored in the natural order, that is the order it is inserted, while retrieving data, no ordering is required, unless you want to reverse the order.

New objects can be inserted into a capped collection.

Existing objects can be updated.

But you can't remove an individual object from the capped collection. Using drop command, you have to remove all the documents. After the drop, you have to recreate the capped collection.

Presently, the maximum size for a capped collection is 1e9(i.e. 1X109) for 32-bit machines. For 64 bit machines, there is no theoretical limit. Practically, it can be extended till your system resources permit.

Capped collections can be used for logging, caching and auto archiving.

Use number of collections instead of one

This omits the requirement if creating index since you are not storing some repeating data on each object.

If applied to a suitable situation, it can enhance the performance.

Metadata

Information about a database is stored in certain collections. They are grouped in system namespace, as

dbname.system.*

The following table shows the collections and what they store

Collections with namespaceDescription
dbname.system.namespaceslist of all namespaces
dbname.system.indexeslist of all indexes
dbname.system.profilestores database profiling information
dbname.system.userslist of users who may access the database
dbname.local.sourcesstores replica slave configuration data and state
dbname.local.sourcesstores replica slave configuration data and state

There are two more options to store metadata :

database.ns files stores additional namespace / index metadata if exists.

Information on the structure of a stored object is stored within the object itself.



ref : https://www.w3resource.com/mongodb/databases-documents-collections.php


반응형
반응형



Mongoose 를 활용한 가입 로그인 처리






코드 내용이 mongoose 를 적용한 부분만 달라진 것임으로 mongoDB 를 활용한 가입과 로그인 처리 (http://3dmpengines.tistory.com/1876) 의 내용을 확인한 다음 이 페이지를 보시면 되겠습니다



mongoose 는 mongoDB의 자유형식으로 데이터를 넣는 것과는 다르게 스키마를 정의(테이블 형태를 정의)하고 해당 형식으로 데이터를 


집어 넣게 되는 형태라서 일괄적인 데이터 처리하는데 더 유용한 구조입니다





스키마를 정의할 수 있는 데이터 타입들





스키마 정의하는 방법





mongoose 를 통해 조회, 저장, 갱신, 제거 처리 메소드





html 내용과 결과는 mongoDB의 결과와 동일합니다


달라진 부분만 굵 폰트로 처리해놓았습니다


서버 nodejs 코드

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var expressErrorHandler = require('express-error-handler');
 
var mongoose = require('mongoose');
 
 
 
var database;
 
var userSchema;
 
 
var userModel;
 
 
//몽고디비에 연결 ,  보통 웹서버 만든 직후 연결 , DB 먼저 연결 되도 상관 없음
//먼저 db를 가져온다 
function connectDB() {
    //localhost 로컬 호스트
    //:27017  몽고디비 포트
    //local db 생성시 만든 폴더 명
    var databaseURL = 'mongodb://localhost:27017/test';
 
    mongoose.Promise = global.Promise;
    mongoose.connect(databaseURL);
 
    database = mongoose.connection;     //db와 연결 시도
 
    database.on('open',         //db 연결 될때의 이벤트
        function ()
        {
            console.log('data base 연결됨 ' + databaseURL);
            
 
            //몽구스는 스키마를 정의하고 해당 스키마에 해당 하는 데이터를 집어넣는 방식으로 테이블과 유사
            userSchema = mongoose.Schema({
                id: String,
                passwords: String,
                nameString,
                
            });
            console.log('userSchema 정의함');
 
            //컬렏션과 스키마를 연결시킴
            userModel = mongoose.model('users', userSchema);
            console.log('userModel 정의함');
        }
    );
 
    database.on('disconnected',         //db 연결 끊길떄
        function () {
            console.log('data base 연결 끊어짐');
        }
    );
 
    database.on('error',         //에러 발생하는 경우
        console.error.bind(console'mongoose 연결 에러')
    );
 
}
 
 
 
 
 
var app = express();      //express 서버 객체
 
 
app.set('port'3000);
app.use(serveStatic(path.join('public', __dirname, 'public')));
 
 
 
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 , 아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized: true
}));
 
 
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
router.route('/process/login').post(
    function (req, res) {
        console.log('process/login 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database) {
            authUser(database, paramID, paramPW,
                function (err, docs) {
                    if (database) {
                        if (err) {
                            console.log('Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>에러발생</h1>');
                            res.end();
                            return;
                        }
 
                        if (docs) {
                            console.dir(docs);
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>Login Success</h1>');
                            res.write('<h1> user </h1>' + docs[0].id + '  :   ' + docs[0].name);
                            res.write('<br><a href="/login.html"> re login </a>');
                            res.end();
 
                        }
                        else {
                            console.log('empty Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>user data not exist</h1>');
                            res.write('<a href="/login.html"> re login</a>');
                            res.end();
                        }
 
                    }
                    else {
                        console.log('DB 연결 안됨');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>databasae 연결 안됨</h1>');
                        res.end();
                    }
 
 
 
                }
            );
        }
    }
);
 
 
router.route('/process/addUser').post(
 
    function (req, res) {
        console.log('process/addUser 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        var paramName = req.body.name || req.query.name;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database) {
            addUser(database, paramID, paramPW, paramName,
                function (err, result) {
                    if (err) {
                        console.log('Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>에러발생</h1>');
                        res.end();
                        return;
                    }
 
                    if (result) {
                        console.dir(result);
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>Add Success</h1>');
                        res.write('<h1> name </h1>' + paramName);
                        res.write('<br><a href="/login.html"> re login </a>');
                        res.end();
                    }
                    else {
                        console.log('추가 안됨 Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>can not add user</h1>');
                        res.write('<a href="/login.html"> re login</a>');
                        res.end();
                    }
 
                }
            );
        }
        else {
            console.log('DB 연결 안됨');
            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
            res.write('<h1>databasae 연결 안됨</h1>');
            res.end();
        }
 
    }
);
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
var authUser = function (db, id, password, callback) {
    console.log('input id :' + id.toString() + '  :  pw : ' + password);
 
    //cmd 에서 db.users  로 썻던 부분이 있는데 이때 이 컬럼(테이블)에 접근은 다음처럼 한다
    /*
    var users = database.collection("users");
    var result = users.find({ "id": id, "passwords": password });
    */
 
    userModel.find({ "id": id, "passwords": password },
        function (err, docs)
        {
            if (err) {
                callback(err, null);
                return;
            }
 
            if (docs.length > 0) {
                console.log('find user [ ' + docs + ' ]');
                callback(null, docs);
            }
            else {
                console.log('can not find user [ ' + docs + ' ]');
                callback(nullnull);
            }
        }
    );
 
};
 
 
 
var addUser = function (db, id, passwords, name, callback) {
    console.log('add User 호출됨' + id + '  , ' + passwords);
 
 
    var user = new userModel({ "id": id, "passwords": passwords, "name"name });
 
    //user 정보를 저장하겠다는 함수
    user.save
    (
        function (err)
        {
            if (err)
            {
                callback(err, null);
                return;
            }
 
            //데이터가 추가됐다면 insertedCount 카운트가 0 보다 큰값이 된다
            console.log('사용자 추가 됨');
            callback(null, user);
        }
    );
 
};
 
 
 
 
var errorHandler = expressErrorHandler(
    { static: { '404''./public/404.html' } }              //404 에러 코드가 발생하면 해당 페이지를 보여주는 예외 미들웨어
);
 
app.use(expressErrorHandler.httpError(404));
app.use(expressErrorHandler);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
        connectDB();        //DB 연결 , DB 연결 먼저해도 상관 없음
    }
);
 



반응형
반응형


하단 정보를 기준으로 가입, 로그인 처리를 테스트 할수 있습니다


mongoDB에 유저 2명 추가된 정보




" " 로 속성값이 들어가있지 않다면 숫자일 수 있으니 mongodb 를 통한 find 함수(검색)시 주의해야합니다


int 형으로 변경하려면 해당 필드를 parseInt로 변경 가능합니다





로그인, 가입 화면 







로그인 & 가입 플로우


  1. 유저가 없다면 Join 으로 회원 가입을 시도

    1. 회원 가입 성공 페이지와 name 을 보여주는 페이지로 이동

    2. re login 버튼으로 로그인 화면으로 이동

  2. 유저가 있다면 login 시도

    1. 로그인이 되면 유저 정보 (id, name 을 보여줌)를 보여주는 페이지로 이동




Login.html

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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>login2</title>
</head>
<body>
 
    <h1>login2</h1>
    <form method="post" action="/process/Login">
        <table>
            <tr>
                <td><label>ID : </label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>PW : </label></td>
                <td><input type="text" name="passwords"></td>
 
 
            </tr>
 
            <tr>
                <td><input type="submit" value="Sumit" name=""></td>
                <td><input type="button" value="Join" onclick="location.href='/addUser.html'"></td>
            </tr>
 
        </table>
 
    </form>
 
</body>
</html>






addUser.html


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
41
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>login2</title>
</head>
<body>
 
    <h1>login2</h1>
    <form method="post" action="/process/addUser">
        <table>
            <tr>
                <td><label>ID : </label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>PW : </label></td>
                <td><input type="text" name="passwords"></td>
 
 
            </tr>
 
            <tr>
                <td><label>name : </label></td>
                <td><input type="text" name="name"></td>
 
 
            </tr>
 
            <tr>
                <td><input type="submit" value="Sumit" name=""></td>
                <td><input type="button" value="Login" onclick="location.href='/Login.html'"></td>
            </tr>
 
        </table>
 
    </form>
 
</body>
</html>








서버 nodejs 코드


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var expressErrorHandler = require('express-error-handler');
 
var mongoClient = require('mongodb').MongoClient;
 
 
 
var database;
 
//몽고디비에 연결 ,  보통 웹서버 만든 직후 연결 , DB 먼저 연결 되도 상관 없음
//먼저 db를 가져온다 
function connectDB() {
    //localhost 로컬 호스트
    //:27017  몽고디비 포트
    //local db 생성시 만든 폴더 명
    var databaseURL = 'mongodb://localhost:27017';
    mongoClient.connect(databaseURL,
        function (err, cluster)
        {
            //이 구문까지 실행되었다면 ongoDB 에 연결된 것
            if (err) {
                console.log('db connect error');
                return;
            }
 
            console.log('db was connected : ' + databaseURL);
 
            database = cluster.db('test');
 
            //var users = database.collection('users');
 
 
        }
    );
 
}
 
 
 
 
 
var app = express();      //express 서버 객체
 
 
app.set('port'3000);
app.use(serveStatic(path.join('public', __dirname, 'public')));
 
 
 
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 , 아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized: true
}));
 
 
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
router.route('/process/login').post(
    function (req, res) {
        console.log('process/login 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database) {
            authUser(database, paramID, paramPW,
                function (err, docs) {
                    if (database)
                    {
                        if (err) {
                            console.log('Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>에러발생</h1>');
                            res.end();
                            return;
                        }
 
                        if (docs) {
                            console.dir(docs);
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>Login Success</h1>');
                            res.write('<h1> user </h1>' + docs[0].id + '  :   ' + docs[0].name);
                            res.write('<br><a href="/login.html"> re login </a>');
                            res.end();
 
                        }
                        else {
                            console.log('empty Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>user data not exist</h1>');
                            res.write('<a href="/login.html"> re login</a>');
                            res.end();
                        }
 
                    }
                    else
                    {
                        console.log('DB 연결 안됨');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>databasae 연결 안됨</h1>');
                        res.end();
                    }
 
 
 
                }
            );
        }
    }
);
 
 
router.route('/process/addUser').post(
 
    function (req, res)
    {
        console.log('process/addUser 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        var paramName = req.body.name || req.query.name;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database)
        {
            addUser(database, paramID, paramPW, paramName,
                function (err, result)
                {
                    if (err)
                    {
                        console.log('Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>에러발생</h1>');
                        res.end();
                        return;
                    }
 
                    if (result) {
                        console.dir(result);
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>Add Success</h1>');
                        res.write('<h1> name </h1>' + paramName);
                        res.write('<br><a href="/login.html"> re login </a>');
                        res.end();
                    }
                    else {
                        console.log('추가 안됨 Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>can not add user</h1>');
                        res.write('<a href="/login.html"> re login</a>');
                        res.end();
                    }
 
                }
            );
        }
        else
        {
            console.log('DB 연결 안됨');
            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
            res.write('<h1>databasae 연결 안됨</h1>');
            res.end();
        }
       
    }
);
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
var authUser = function (db, id, password, callback) {
    console.log('input id :' + id.toString() + '  :  pw : ' + password);
 
    //cmd 에서 db.users  로 썻던 부분이 있는데 이때 이 컬럼(테이블)에 접근은 다음처럼 한다
    var users = database.collection("users");
 
    //찾고자 하는 정보를 입력해준다
    //var result = users.find({ name: id, passwords: password });
    //var result = users.find({ "name": id, "passwords":password });
    //var result = users.find({ "name": id , "passwords": password });
    //var result = users.find({});
 
    var result = users.find({ "id": id, "passwords": password });
 
    result.toArray(
        function (err, docs) {
            if (err) {
                callback(err, null);
                return;
            }
 
            if (docs.length > 0) {
                console.log('find user [ ' + docs + ' ]');
                callback(null, docs);
            }
            else {
                console.log('can not find user [ ' + docs + ' ]');
                callback(nullnull);
            }
        }
 
    );
 
};
 
 
 
var addUser = function (db, id, passwords, name, callback) {
    console.log('add User 호출됨' + id + '  , ' + passwords);
    var users = db.collection('users');
 
    //컬렉션에 데이터 추가할때는 배열 형태로 집어 넣는다
    users.insertMany([{ "id": id, "passwords": passwords, "name"name }],
        function (err, result) {
            if (err) {
                callback(err, null);
                return;
            }
 
            //데이터가 추가됐다면 insertedCount 카운트가 0 보다 큰값이 된다
            if (result.insertedCount > 0) {
                console.log('사용자 추가 됨' + result.insertedCount);
                callback(null, result);
            }
            else {
                console.log('사용자 추가 안됨' + result.insertedCount);
                callback(nullnull);
 
            }
 
        }
    );
 
};
 
 
 
 
var errorHandler = expressErrorHandler(
    { static: { '404''./public/404.html' } }              //404 에러 코드가 발생하면 해당 페이지를 보여주는 예외 미들웨어
);
 
app.use(expressErrorHandler.httpError(404));
app.use(expressErrorHandler);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
        connectDB();        //DB 연결 , DB 연결 먼저해도 상관 없음
    }
);
 






반응형
반응형



몽고디비 기본 사용법과 데이터 조회





  • connect : 몽고디비 데이터 베이스 서버에 접속

  • collection : 컬렉션 참조( 테이블 참조와 유사)

  • find : 문서(데이터) 찾기




몽고 db 서버를 띄우기  


mongod --dbpath 원하는경로








다음처럼 데이터 값을 추가합니다





데이터가 잘 들어갔는지 확인




이것을 웹 서버에서 조회해서 존재한다면 로그인 성공으로 간주 그렇지 않으면 다시 로그인 화면으로 오는 웹 서버를 만들어보겠습니다






로그인 처리 화면



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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>login2</title>
</head>
<body>
 
    <h1>login2</h1>
    <form method="post" action="/process/Login">
        <table>
            <tr>
                <td><label>ID : </label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>PW : </label></td>
                <td><input type="text" name="passwords"></td>
 
 
            </tr>
 
            <tr>
                <td><input type="submit" value="Sumit" name=""></td>
            </tr>
 
        </table>
 
    </form>
 
</body>
</html>


login.html



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>404 Error</title>
</head>
<body>
    <h3>404 Error</h3>
    <br />
 
    <p>Can not find a page</p>
 
    <a href="/"> Home  </a>
</body>
</html>


404.html
오류페이지 처리를 위한 html






서버코드 (nodejs)

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var expressErrorHandler = require('express-error-handler');
 
var mongoClient = require('mongodb').MongoClient;
 
 
 
var database;
 
//몽고디비에 연결 ,  보통 웹서버 만든 직후 연결 , DB 먼저 연결 되도 상관 없음
//먼저 db를 가져온다 
function connectDB()
{
    //localhost 로컬 호스트
    //:27017  몽고디비 포트
    //local db 생성시 만든 폴더 명
    var databaseURL = 'mongodb://localhost:27017';
    mongoClient.connect(databaseURL,
        function (err, db)
        {
            if (err)
            {
                console.log('db connect error');
                return;
            }
 
            console.log('db was connected : ' + databaseURL);
            database = db;          //이 구문까지 실행되었다면 ongoDB 에 연결된 것
        }
    );
 
}
 
 
 
 
 
var app = express();      //express 서버 객체
 
 
app.set('port'3000);
app.use('public', serveStatic(path.join( __dirname, 'public')));
 
 
 
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
//루트 경로 세팅
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 , 아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized: true
}));
 
 
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
//로그인 처리
router.route('/process/login').post(
    function (req, res)
    {
        console.log('process/login 호출됨');
        var paramID = req.body.id || req.query.id;
        var paramPW = req.body.passwords || req.query.passwords;
        console.log('paramID : ' + paramID + ', paramPW : ' + paramPW);
 
        if (database)
        {
            authUser(database, paramID, paramPW,
                function (err, docs)
                {
                    if (database)
                    {
                        if (err)
                        {
                            console.log('Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>에러발생</h1>');
                            res.end();
                            return;
                        }
 
                        if (docs)
                        {
                            console.dir(docs);
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>Login Success</h1>');
                            res.write('<h1> user </h1>' + docs[0].name);
                            res.write('<br><a href="/login.html"> re login </a>');
                            res.end();
 
                        }
                        else
                        {
                            console.log('empty Error!!!');
                            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                            res.write('<h1>user data not exist</h1>');
                            res.write('<a href="/login.html"> re login</a>');
                            res.end();
                        }
 
                    }
                    else
                    {
                        console.log('empty Error!!!');
                        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
                        res.write('<h1>databasae 연결 안됨</h1>');
                        res.end();
                    }
                    
                    
 
                }
            );
        }
    }
);
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 

//로그인 확인
var authUser = function (db, id, password, callback)
{
    console.log('input id :' + id + '  :  pw : ' + password);
 
    //cmd 에서 db.users  로 썻던 부분이 있는데 이때 이 컬럼(테이블)에 접근은 다음처럼 한다
    var users = db.db('test').collection("users");
 
    //찾고자 하는 정보를 입력해준다
    //var result = users.find({ name: id, passwords: password });
    //var result = users.find({ "name": id, "passwords":password });
    var result = users.find({ "name": id, "passwords"parseInt(password) });
    //var result = users.find({});
 
    result.toArray(
        function (err, docs)
        {
            if (err) {
                callback(err, null);
                return;
            }
 
            if (docs.length > 0)
            {
                console.log('find user [ ' + docs + ' ]');
                callback(null, docs);
            }
            else
            {
                console.log('can not find user [ ' + docs + ' ]');
                callback(nullnull);
            }
        }
    
    );
    
};
 
 
var errorHandler = expressErrorHandler(
    { static: { '404''./public/404.html' } }              //404 에러 코드가 발생하면 해당 페이지를 보여주는 예외 미들웨어
);
 
app.use(expressErrorHandler.httpError(404));
app.use(errorHandler );
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
        connectDB();        //DB 연결 , DB 연결 먼저해도 상관 없음
    }
);
 




반응형
반응형


MongoDB는 대표적인 NoSQL이며 JSON 형식으로 데이터를 저장하고 SQL과 유사한 쿼리로 수행 가능한 문서 지향 데이터베이스입니다. 

 

먼저 아래의 주소에서 몽고디비 설치파일을 다운로드 받습니다.

https://www.mongodb.com/download-center?jmp=nav#community



다운로드 받은 "mongodb-win32-x86_64-2008plus-ssl-3.4.9-signed.msi" 파일을 실행하여 몽고디비를 설치합니다.

아무런 변경없이 기본값으로 설치하면 C:\Program Files\MongoDB\Server\3.4\bin 경로에 설치됩니다.


다음을 눌러 설치하면 되고 설치 경로는 원하는 곳으로 설정하면 됩니다



시스템 환경변수에 path 부분 변수 값에 설치된 경로 bin 까지를 추가해줍니다


그리고 cmd 를 띄우고 하단 명령어를 실행시키는데 이것은 데이터베이스를 서비스하기 위한 명령어로 

이것을 실행해놔야 데이터 베이스가 동작하게 되는 것입니다



mongod --dbpath /Users/현재사용자계정이름/database/local


/Users/현재사용자계정이름/database/local 이것은 사용자 계정 폴더로 이 폴더가 있어야 동작을 합니다

/database/local  이 폴더가 없다면 직접 만들면 됩니다







이렇게가지 수행했다면 이제는 데이터베이스 서버(서비스)가 실행된것입니다


제일 하단에 27017 포트번호는 이쪽 번호로 연결이 들어오기를 기다리고 있다는 뜻입니다


다른 cmd 를 띄워 mongo를 입력하면 mongodb://127.0..0.1:27017 로 접속하는 것을 볼 수 있습니다


이렇게 나온다면 이후 필요한 작업을 할수 있습니다


cmd 창에서 계속 다음을 입력

use local      DB가 local 을 사용할수 있게 변경 하는 것 , 상단에 계정 하단에  /database/local 폴더를 만들었는데 이 local 폴더를 말함


db.users.insert({name:'3dmp', number:123456789})     이 명령어를 입력하면 (즉 객체를 통채로 저장, no-sql 방식임)

db 아래 users 컬렉션(테이블과 유사)이 없으면 추가 되고 그다음 객체 {name:'3dmp', number:123456789} 를 추가하는 명령


다음 결과를 보여주는데 이것은  데이터가 추가가 됐다는 것을 의미

WriteResult({ "nInserted" : 1 })


cmd 에서 이어서 다음을 입력..

1
2
3
4
5
6
7
8
9
10
11
12
> db.users                        //이것은 users컬렉션(테이블)까지
local.users
> db.users.find()                 //users 하단의 정보를 찾는 것
"_id" : ObjectId("5aeb379d4482a0efc7ac86e5"), "name" : "3dmp""number" : 123456789 }
> db.users.find().pretty()    //출력 결과를 좀 더 보기 좋게
{
        "_id" : ObjectId("5aeb379d4482a0efc7ac86e5"), //id 속성이 자동으로 만들어짐
        "name" : "3dmp",
        "number" : 123456789
}
>
 



cmd 창에서 입력한 전문





cmd 창으로 접근하여 아래처음 바로 원소를 추가하면 database 네임이 test 로 만들어지고 nodejs 에서 처음 database name 에 접근하기 위해선 위 코드와는 약간 다르게 접근해야 오류가 안나는데 접근 방식은 하단 코드를 참조하면 됩니다

db.users.insert({name:'3dmp', number:123456789}) 




몽고디비에 연결하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//몽고디비에 연결 ,  보통 웹서버 만든 직후 연결 , DB 먼저 연결 되도 상관 없음
//먼저 db를 가져온다 
function connectDB()
{
    //localhost 로컬 호스트
    //:27017  몽고디비 포트
    //local db 생성시 만든 폴더 명
    var databaseURL = 'mongodb://localhost:27017';
    mongoClient.connect(databaseURL,
        function (err, db)
        {
            if (err)
            {
                console.log('db connect error');
                return;
            }
 
            console.log('db was connected : ' + databaseURL);
            database = db;          //이 구문까지 실행되었다면 ongoDB 에 연결된 것
        }
    );
 
}
 




몽고디비에 연결이 된 이후 database 명으로 컬렉션(테이블) 가져오기

(바로 데이터를 추가하면 test db 가 만들어짐) 으로 연결은 애초에 connect 에서 뒤에  /test 를 붙여도 되는데  


var databaseURL = 'mongodb://localhost:27017/test';



주소 뒤에 test 가 있든 없든 db.db('test') 이 처럼 db를 한번은 가져와야함



1
2
3
4
5
//cmd 에서 db.users  로 썻던 부분이 있는데 이때 이 컬럼(테이블)에 접근은 다음처럼 한다
    var users = db.db('test').collection("users");
    //찾고자 하는 정보를 입력해준다
    //var result = users.find({ name: id, passwords: password });
    var result = users.find({});




반응형
반응형


파일을 서버의 폴더로 업로드하기





브라우저에서 파일 올리기 요청을 하면 서버에 설정해둔 경로로 파일을 업로드한다


이때 파일 업로드를 위해 사용되는 주 모듈은


1
2
3
var multer = require('multer');     //파일 업로드하기 위한 모듈
var fs = require('fs');             //파일 시스템
var cors = require('cors');         //다중 서버로 접속하게 해주는 기능을 제공, 다른 ip 로 다른서버에 접속
cs

이렇게 3개이다



아래 지정한 경로대로 서버에 업로드할 파일을 전송하게 된다



photo.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>upload file</title>
</head>
    <h1>upload file</h1
 
<form method="post" action="/process/photo" enctype="multipart/form-data">
    <table>
        <tr>
            <td><label>file</label></td>
            <td><input type="file" name="photo" /></td<!--photo 이 이름이 서버쪽으로 요청파라미터 이름으로 들어가게됨-->
        </tr>
    </table>
    <input type="submit" value="upload" name="summit" />
</form>
<body>
 
</body>
</html>





photo.html 실행시킨 모습







서버 nodejs 코드


서버코드인데 중간에 cors 를 사용한 부분이 있는데 이것은 웹페이지를 연서버가 있으면 그 서버로만 접속하게끔 되어 있는 보안 정책이 있는데 

파일 업로드하기 위해선 이것을  꼭 서버에만 접속해선 기능을 만들기 힘들수 있음으로 cors 를 상용해 이 정책을 우회한다


var cors = require('cors');         //다중 서버로 접속하게 해주는 기능을 제공, 다른 ip 로 다른서버에 접속

app.use(cors());                      //다중 접속을 위한 미들웨에



파일이 저장될 경로 세팅

1
app.use('/uploads',serveStatic(path.join(__dirname, 'uploads')));



웹은 기본적으로 '동일출저정책(Same Origin Policy, SOP)' 정책을  따른다.


이는 보안을 위한 기본정책으로, SOP는 하나의 출처(Origin)에서 로드된 문서나 스크립트가 다른 출처에 존재하는 리소스와 상호작용하지 못하도록 제약을 두는 것이다.


그런데, 간혹 이런 제약이 웹 응용프로그램을 만드는데 걸림돌이 되기도 한다.

Ajax 통신이 활발해 지고, 다른 사이트에 존재하는 Open API와 상호통신이 필요한 경우와 매쉬업(Mash-up)으로 새로운 2차 응용물을 개발하게 되면서.. 등등.. 이는 간혹 걸림돌이 된다.


근래 Node.JS로 서버를 만들고, Aptana Studio 자체 내장 웹서버로 몇 가지 테스트를 하고 있는데, 두 환경은 서로 다른 별도의 포트(Port)에서 웹을 동작시키기 때문에 여지없이 Cross Domain 문제가 발생한다.




호출하는 모든 웹 리소스를, Node.JS로부터 다운로드 받아서 실행하는 구조로 HTTP 서버를 구현하면 SOP 정책을 따를 수 있겠으나, 그렇지 못한 개발환경도 있을 터이고, 실제 서비스 환경에서는 더욱이 서로 다른 도메인(포트 포함)간 상호작용이 필요할 것이다.


SOP 정책을 우회하기 위해서는, 서버 측의 설정이 필요한데, 위 그림에서 친철히 안내하고 있는 것처럼 Access-Control-Allow-Origin 헤더, 즉 크로스 도메인으로 허용할 도메인 목록이 헤더로 제공되어야 한다.

이것을 CORS(Cross-Origin Resource Sharing)를 이용한 SOP 정책 우회라 한다.







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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
 
var multer = require('multer');     //파일 업로드하기 위한 모듈
var fs = require('fs');             //파일 시스템
var cors = require('cors');         //다중 서버로 접속하게 해주는 기능을 제공, 다른 ip 로 다른서버에 접속
var app = express();      //express 서버 객체
 
 
app.set('port'3000);
app.use(serveStatic(path.join(__dirname, 'public')));
app.use('/uploads',serveStatic(path.join(__dirname, 'uploads')));
 
 
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 , 아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized: true
}));
 
 
 
 
//파일을 올리기위한 세팅 시작
 
 
//다중 접속을 위한 미들웨에
app.use(cors());
 
//파일 업로드를 위한 설정
var fileStorage =  multer.diskStorage(
    {
        destination: function (req,file, callback)
        {
            callback(null'uploads');      //목적지 폴더 지정 : 'uploads' 를 목적지로 정한다(이쪽으로 파일이 오게됨)
        },
        filename: function (req, file, callback)
        {
            //올린 파일명이 기존과 같으면 덮어씌워짐으로 시간을 기준으로 다른 파일로 저장되게끔 처리한다
   
            var extention = path.extname(file.originalname);
            var basename = path.basename(file.originalname, extention);        //확장자 .jpg 만 빠진 파일명을 얻어온다
            var fname = basename + Date.now() + extention;
            callback(null, fname);  
 
        }
 
    }
);
 
var upload = multer(
    {
        storage: fileStorage,
        limits:
            {
                files: 10,                      //10개까지
                fileSize: 1024 * 1024 * 1024    //한번 업로드 할때 최대 사이즈
            }
    }
);
 
//파일을 올리기위한 세팅 끝
//이제 이것을 갖고 라우터를 통해서 파일을 업로드 할수 있게끔 해야함
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
 
 
//upload.array : Accept an array of files, all with the name fieldName. 
//Optionally error out if more than maxCount files are uploaded.
//The array of files will be stored in req.files. 
//('photo',1) : photo 라고 지정한 파일이 있다면 1개를 전송하라는 의미
router.route('/process/photo').post(upload.array('photo',1),
    function (req, res)
    {
        console.log('/process/photo 함수가 호출됨');
        var files = req.files;
        if (files.length > 0)
        {
            console.dir(files[0]);
        }
        else
        {
            console.log('파일이 없음');
        }
 
 
        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
        res.write('<h1>file upload complete</h1>');
        if (Array.isArray(files))
        {
            files.forEach(
                function (elem)
                {
                    res.write('<h1>' + elem.originalname + '</h1>');
                    res.write('<h1>' + elem.filename + '</h1>');
                    res.write('<h1>' + elem.mimetype + '</h1>');
                    res.end();
                      
                }
            );
        }
 
        
    }
);
 
 
//http://localhost:3000/process/product 이 주소로 치면 라우터를 통해 바로 여기로 올 수 있다
router.route('/process/product').get(
    function (req, res) {
        console.log('/process/product  라우팅 함수 실행');
 
        //세션정보는 req.session 에 들어 있다
        if (req.session.user)       //세션에 유저가 있다면
        {
            res.redirect('/product.html');
        }
        else {
            res.redirect('/login2.html');
 
        }
    }
);
 
router.route('/process/login').post(                      //설정된 쿠키정보를 본다
    function (req, res) {
        console.log('/process/login 라우팅 함수호출 됨');
 
        var paramID = req.body.id || req.query.id;
        var pw = req.body.passwords || req.query.passwords;
 
        if (req.session.user) {
            console.log('이미 로그인 되어 있음');
 
            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
            res.write('<h1>already Login</h1>');
            res.write('[ID] : ' + paramID + ' [PW] : ' + pw);
            res.write('<a href="/process/product">Move</a>');
            res.end();
 
        } else {
            req.session.user =
                {
                    id: paramID,
                    pw: pw,
                    name'UsersNames!!!!!',
                    authorized: true
                };
            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
            res.write('<h1>Login Success</h1>');
            res.write('[ID] : ' + paramID + ' [PW] : ' + pw);
            res.write('<a href="/process/product">Move</a>');
            res.end();
        }
    }
);
 
router.route('/process/logout').get(                      //설정된 쿠키정보를 본다
    function (req, res) {
        console.log('/process/loginout 라우팅 함수호출 됨');
 
        if (req.session.user) {
            console.log('로그아웃 처리');
            req.session.destroy(
                function (err) {
                    if (err) {
                        console.log('세션 삭제시 에러');
                        return;
                    }
                    console.log('세션 삭제 성공');
                    //파일 지정시 제일 앞에 / 를 붙여야 root 즉 public 안에서부터 찾게 된다
                    res.redirect('/Login2.html');
                }
            );          //세션정보 삭제
 
        } else {
            console.log('로긴 안되어 있음');
            res.redirect('/Login2.html');
        }
 
 
 
    }
);
 
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
app.all('*',
    function (req, res) {
        res.status(404).send('<h1> 요청 페이지 없음 </h1>');
    }
);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 





파일을 올리고 나면 다음처럼 파일이 uploads 폴더에 저장되어 있는 것을 확인할 수 있다







정책 일부 ref : http://m.mkexdev.net/339

반응형
반응형


쿠키는 브라우저에 정보들을 저장하는 방식으로 진행된다



세션은 정보를 서버에 저장해놓고 키를 통해서 브라우저와 통신하게 되는데

이때 필요한 세션정보는 쿠키로 저장한다 => (connect.id)



Route 에 의한 경로 이동을 하게 되는데 html 에서는 action="path" 에 있는 경로나  a href="path" 에 있는 경로를 보고


서버(Nodejs) 로 해당 경로를 던지게 되면 서버(Nodejs) 에서는 해당 경로를 Route 로 등록해놓았을 경우


해당 경로에 대한 함수 처리를 하게 되는 방식으로 동작한다




세션을 통한 전체 동작은 다음 처럼 이루어진다


로그인하여 product.html 에 있는 내용을 보기위한 과정이라고 보면된다(로그인처리하면서 정보를 세션에 저장)

다 보았으면 로그아웃하면서 세센 정보를 삭제한다



(html 파일들은 public  폴더 아래 있다고 가정한다)







login2.html (로그인 담당 페이지)



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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>login2</title>
</head>
<body>
 
    <h1>login2</h1>
    <form method="post" action="/process/Login">
        <table>
            <tr>
                <td><label>ID : </label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>PW : </label></td>
                <td><input type="text" name="passwords"></td>
 
 
            </tr>
 
            <tr>
                <td><input type="submit" value="Sumit" name=""></td>
            </tr>
 
        </table>
 
    </form>
 
</body>
</html>






Product.html  (간단한 shoes 정보를 보여준다고 가정하는 페이지)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>product</title>
</head>
<body>
    <h3>product page - Now sale</h3>
    <br />
 
    <p>Nice shoes 1</p>
    <p>Nice shoes 2</p>
    <p>Nice shoes 3</p>
    <p>Nice shoes 4</p>
    <p>Nice shoes 5</p>
 
    <a href="/process/logout"> Logout  </a>
</body>
</html>






서버코드 (Nodejs.js)



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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
 
var app = express();      //express 서버 객체
 
var bodyParser_post = require('body-parser');       //post 방식 파서
 
app.set('port'3000);
 
 
//미들웨어들 등록 시작, 아래 미들웨어들은 내부적으로 next() 가실행됨
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
//쿠키와 세션을 미들웨어로 등록한다
app.use(cookieParser());
 
//세션 환경 세팅
//세션은 서버쪽에 저장하는 것을 말하는데, 파일로 저장 할 수도 있고 레디스라고 하는 메모리DB등 다양한 저장소에 저장 할 수가 있는데
app.use(expressSession({
    secret: 'my key',           //이때의 옵션은 세션에 세이브 정보를 저장할때 할때 파일을 만들꺼냐 //아니면 미리 만들어 놓을꺼냐 등에 대한 옵션들임
    resave: true,
    saveUninitialized:true
}));
 
 
 
//라우트를 미들웨어에 등록하기 전에 라우터에 설정할 경로와 함수를 등록한다
//
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
//http://localhost:3000/process/product 이 주소로 치면 라우터를 통해 바로 여기로 올 수 있다
router.route('/process/product').get(
    function (req, res)
    {
        console.log('/process/product  라우팅 함수 실행');
 
        //세션정보는 req.session 에 들어 있다
        if (req.session.user)       //세션에 유저가 있다면
        {
            res.redirect('/product.html');
        }
        else
        {
            res.redirect('/login2.html');
 
        }
    }
);
 
router.route('/process/login').post(                      //설정된 쿠키정보를 본다
    function (req, res) {
        console.log('/process/login 라우팅 함수호출 됨');
 
        var paramID = req.body.id || req.query.id;
        var pw = req.body.passwords || req.query.passwords;
 
        if (req.session.user) {
            console.log('이미 로그인 되어 있음');
 
            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
            res.write('<h1>already Login</h1>');
            res.write('[ID] : ' + paramID + ' [PW] : ' + pw);
            res.write('<a href="/process/product">Move</a>');
            res.end();
 
        } else {
            req.session.user =
                {
                    id: paramID,
                    pw: pw,
                    name'UsersNames!!!!!',
                    authorized: true
                };
            res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
            res.write('<h1>Login Success</h1>');
            res.write('[ID] : ' + paramID + ' [PW] : ' + pw);
            res.write('<a href="/process/product">Move</a>');
            res.end();
        }
    }
);
 
router.route('/process/logout').get(                      //설정된 쿠키정보를 본다
    function (req, res) {
        console.log('/process/loginout 라우팅 함수호출 됨');
 
        if (req.session.user) {
            console.log('로그아웃 처리');
            req.session.destroy(
                function (err) {
                    if (err) {
                        console.log('세션 삭제시 에러');
                        return;
                    }
                    console.log('세션 삭제 성공');
                    //파일 지정시 제일 앞에 / 를 붙여야 root 즉 public 안에서부터 찾게 된다
                    res.redirect('/Login2.html');
                }
            );          //세션정보 삭제
 
        } else {
            console.log('로긴 안되어 있음');
            res.redirect('/Login2.html');
        }
 
 
     
    }
);
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
app.all('*',
    function (req, res) {
        res.status(404).send('<h1> 요청 페이지 없음 </h1>');
    }
);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 


반응형
반응형

설치해야할 모듈

npm install cookie-parser --save



쿠키 : 브라우저에 저장정보

세션 : 서버에 저장정보



필요한 모듈

var cookieParser = require('cookie-parser');





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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
var cookieParser = require('cookie-parser');
 
var app = express();      //express 서버 객체
 
var bodyParser_post = require('body-parser');       //post 방식 파서
 
app.set('port'3000);
 
 
//미들웨어들 등록 시작, 아래 미들웨어들은 내부적으로 next() 가실행됨
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
 
app.use(cookieParser());
 
 
 
 
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
 
//라우터를 통해 쿠키 정보를 저장할 수 있음
router.route('/process/setUserCookie').get(
    function (req, res)
    {
        console.log('/process/setUserCookie : 라우팅 함수 호출 ');
 
        //웹서버에서 웹 브라우저에 저장할 정보를 세팅한다
        res.cookie(
            'user', { id: '1004'name'kim', authorized: true }
        );
        res.redirect('/process/showCookie');    //페이지 리다이렉트
    }
);
 
 
// 웹브라우저에서 서버쪽에 어떤 요청을 할대 갖고 있는 쿠키정보를 넘겨준다, 이 정보는 req,cookies 에서 확인할 수 있다
router.route('/process/showCookie').get(
    function (req, res) {
        console.log('/process/showCookie : 라우팅 함수 호출 ');
 
        //req.cookies 이것은 서버에 있는 것이 아닌 클라에서 요청했을때 넘어온 req 객체에 있는  웹브라우저 쿠키 정보이다
        //즉 클라이언트에서 서버 접속시 브라우저에서 보내준 정보인데 이것을 사전에 값을 세팅하여 다시 클라로 보내
        //해당 내용을 저장하라고 send 할 수 있다
        res.send(req.cookies);
    }
);
 
 
 
router.route('/process/login/:name').post(                      //설정된 쿠키정보를 본다
    function (req, res) {
        console.log('/process/login:name 라우팅 함수에서 받음');
 
        var paramName = req.params.name;
 
        var paramID = req.body.id || req.query.id;
        var pw = req.body.passwords || req.query.passwords;
 
        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
        res.write(paramName + ' ,  ' + paramID + " : " + paramID);
        res.end();
    }
);
 
 
//라우터 미들웨어 등록하는 구간에서는 라우터를 모두  등록한 이후에 다른 것을 세팅한다
//그렇지 않으면 순서상 라우터 이외에 다른것이 먼저 실행될 수 있다
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
app.all('*',
    function (req, res) {
        res.status(404).send('<h1> 요청 페이지 없음 </h1>');
    }
);
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 






브라우저에 localhost:3000/process/setUserCookie 를 입력하여 클라로부터 받은 쿠키 정보에 저장할 값을 서버에서 세팅한다음

다시 클라로 전송 하여 브라우저에서 정보를 갖고있게 한다


결과화면 




쿠키 정보에서 showCookie , 쿠키 값을 보면 이름이 user인 값이 브라우저에 저장된 것을 볼 수 있다








반응형
반응형

URL 요청파라미터

post 방식 요청 path (Router) 의 맨 뒤의 경로를 값으로 쓰겠다는 것



형식 :

path/:name


클라이언트에서 요청할떄 name 으로 요청 보냄




nodejs : Express, Router 라우터로 경로에 대한 분기 처리

http://3dmpengines.tistory.com/1867


글을 먼저 참고



전과 달라진 부분만 강조하여 표시하면



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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>로긴</title>
</head>
<body>
 
    <h1>로그인 Route</h1>
    <br />
    <form method="post" action="/process/Login/add">
        <table>
            <tr>
                <td><label>아이디</label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>비번</label></td>
                <td><input type="text" name="passwords"></td>
            </tr>
        </table>
        <td><input type="submit" value="전송" name=""></td>
    </form>
 
</body>
</html>

 

submit 을 할때 경로 끝에 /add 를 붙여 이것이 경로가 아닌 파라미터로 넘어가게 한다






서버측 코드


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
 
var app = express();      //express 서버 객체
 
var bodyParser_post = require('body-parser');       //post 방식 파서
 
app.set('port'3000);
 
 
//미들웨어들 등록 시작, 아래 미들웨어들은 내부적으로 next() 가실행됨
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended: false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
router.route('/process/login/:name').post(                      //이 경로로 들어오는 것은 post 방식으로 처리
    function (req, res) {
        console.log('/process/login:name 라우팅 함수에서 받음');
 
        var paramName = req.params.name; //이런 방식으로 경로에서 요청파라미터의 값을 얻어온다
 
        var paramID = req.body.id || req.query.id;
        var pw = req.body.passwords || req.query.passwords;
 
        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
        res.write(paramName + ' ,  ' + paramID + " : " + paramID);
        res.end();
    }
);
 
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 




입력 화면 :






전송 클릭



결과 화면 : 




실행 결과를 보면 add 가 post 보내기 방식과 Router를 통해 경로중 끝값인 :name 에 매칭되어 보내졌다는 결과를 볼 수 있다



파라미터를 보내는 방식은 get ,post , 라우터를 통한 파라미터 보내기 방식인 3가지가 있다






등록하지 않은 경로에 대한 예외처리는 다음 처럼 할 수 있다





상단 서버코드에서 접근 가능한 경로외의 경로 전체에  대한 코드만 추가해주면 되는데 다음과 같다

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
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
router.route('/process/login/:name').post(                      //이 경로로 들어오는 것은 post 방식으로 처리
    function (req, res) {
        console.log('/process/login:name 라우팅 함수에서 받음');
 
        var paramName = req.params.name;
 
        var paramID = req.body.id || req.query.id;
        var pw = req.body.passwords || req.query.passwords;
 
        res.writeHead(200, { "Content-Type""text/html;characterset=utf8" });
        res.write(paramName + ' ,  ' + paramID + " : " + paramID);
        res.end();
    }
);
 
app.use('/', router);       //라우트 미들웨어를 등록한다


app.all('*',
    function (req, res)
    {
        res.status(404).send('<h1> 요청 페이지 없음 </h1>');
    }
);
 
 





'*' 정상경로가 아닌 모든 경우의 경우  res.status(..)  로 예외처리를 할 수 있다




위 이미지에 표시된 경로는 서버 public 폴더 안에 없는 경로임으로 위와 같은 예외처리 문장이 나타나게 됩니다



좀 더 예외처리를 향상되게 하고 싶다면 'express-error-handler'  모듈을 사용하여 예외처리를 할 수 있습니다


이 기능은 해당 에러에 대한 에러 페이지를 미리 만들놔 그 페이지를 보여주는 방식입니다






Path 를 찾지 못하는 경우의 flow






반응형
반응형






라우터

라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능), Express 가 제공



특정 경로에 대한 요청은 router.route('/process/login').post(...) 이런 방식으로 '해당 경로로 들어 온것에 대해 미들웨어'를 발동 시킬 수 있다


'해당 경로 명과 .html 등의 확장명 붙어 있는 것은 서로 다른 경로로 인식함'




브라우저로 먼저 라우터와 상관 없는 메인 페이지인 Login.html 페이지로 들어간다


http://localhost:3000/Login.html



이 웹페이지의 코드는 아래와 같다


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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>로긴</title>
</head>
<body>
 
    <h1>로그인 Route</h1>
    <br />
    <form method="post" action="/process/Login">
        <table>
            <tr>
                <td><label>아이디</label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>비번</label></td>
                <td><input type="text" name="passwords"></td>
            </tr>
        </table>
        <td><input type="submit" value="전송" name=""></td>
    </form>
 
</body>
</html>


Login.html




위 그림 처럼 id, pw 를 입력한다음


 전송 버튼을 누르면 


id, pw 가 서버로 전송된다




이때 위 코드에서도 나와 있듯이 form="post' 방식으로 전달 하는데 경로를 /process/Login  으로 설정해 


서버에 전송 할 수 있다






이런 경우 nodejs.js 서버 코드에서  /process/Login 경로로 온 요청에 대해선 Router 를 통해 이에 대해서만 미들웨어가 호출되게 할 수 있다









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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
 
var app = express();      //express 서버 객체
 
var bodyParser_post = require('body-parser');       //post 방식 파서
 
app.set('port'3000);
 
 
//미들웨어들 등록 시작, 아래 미들웨어들은 내부적으로 next() 가실행됨
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended:false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
 
//라우터를 사용 (특정 경로로 들어오는 요청에 대하여 함수를 수행 시킬 수가 있는 기능을 express 가 제공해 주는것)
var router = express.Router();
 
router.route('/process/login').post(         //이 경로로 들어오는 것은 post 방식으로 처리
    function (req, res)
    {
        console.log('/process/login 라우팅 함수에서 받음');
        var paramID = req.body.id || req.query.id;
        var pw = req.body.passwords || req.query.passwords;
 
        res.writeHead(200, { "Content-Type":"text/html;characterset=utf8" });
        res.write(paramID + " : " + paramID);
        res.end();
    }
);
 
app.use('/', router);       //라우트 미들웨어를 등록한다
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 





결과 화면 :



라우터를 통해 처리된 결과를 클라이언트에게 보내준 정보에 출력 결과



반응형
반응형
먼저 index.html 인데 이 html 파일에서는 로그인을 위한 아이디와 pw 를 입력 받는 페이지로 구성했다

name="id" , name="passwords"

summit 버튼을 통해서 위 두개의 정보 값이 post 방식으로 서버에 넘어간다


로그인을 위한 index.html

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
<!DOCTYPE html>
 
<html>
<head>
    <meta charset="utf-8" />
    <title>로긴</title>
</head>
<body>
 
    <h1>로그인</h1>
     <br />
     <form method="post">
         <table>
             <tr>
                 <td><label>아이디</label></td>
                 <td><input type="text" name="id"></td>
             </tr>
             <tr>
                 <td><label>비번</label></td>
                 <td><input type="text" name="passwords"></td>
             </tr>
         </table>
         <td><input type="submit" value="전송" name=""></td>
     </form>
 
</body>
</html>






서버 nodejs.js 코드
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
 
var app = express();      //express 서버 객체
 
var bodyParser_post = require('body-parser');       //post 방식 파서
 
app.set('port'3000);
 
 
//미들웨어들 등록 시작, 아래 미들웨어들은 내부적으로 next() 가실행됨
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended:false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
//이건 사용자 정의형태로 미들웨어를 제작
app.use(
    function (req, res, next) {
        console.log('middle wared was called : first');
        //res.redirect('http://google.co.kr');
 
        //req 여러 정보를 얻어 올 수 있는데 그중
        //요청받은 request 정보중에서 User-Agent 정볼를 따로 분리하여 갖어올 수 있다
        var userAgent = req.header('User-Agent');
 
        //요청파라미터는 get 방식인 req.query 에 들어오게 된다
        //post 방식은 body로 들어오게된다
        //name 은 정해져있는 명칭
 
        var paramName = req.body.id || req.query.id;
        var paramNamePW = req.body.passwords || req.query.passwords;
 
        //응답 보내기
        res.send('<h3>response from server!!7!! : ' + userAgent + '[' + paramName + ' : ' + paramNamePW  + '] </h3>');
    }
);
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
);
 




위 소스에서 굵은 글씨로 id 와 pw를 얻어올 수 있다


실행 절차는 먼저 이미지의 주소로 접근한다


index.html 파일은 public 폴더 밑에 두면 된다









전송 버튼을 누른다음 클라이언트가 받게 되는 정보는 아래와 같다 [ ...... : ....... ]








반응형
반응형

get 방식


특정 경로에 있는 폴더(public 폴더)를 요청에 의해서 바로 파일을 가져올 수 있는 기능을 제공 해주는 모듈 "serve-static" 이라는 것이 존재






우선 프로젝트 폴더에 public 이란 폴더를 만들어 놓는다


public 폴더안에 /game0.jpg 가 있는 상태라 가정




이럴 경우 서버를 실행 시킨다음에 브라우저에서


http://localhost:3000/game0.jpg 를 입력하면 해당 이미지가 열리는 것을 알 수 있다



만약  public/image/game0.jpg 로 배치해 놓았다면


http://localhost:3000/image/game0.jpg  로 입혁하면 된다



또는 서버에서 


res.end("<img src='/game0.jpg' ");


를 입력하여 곧바로 이미지를 보낼 수 있다





소스코드 : 


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
41
42
43
44
45
46
47
48
49
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
 
var app = express();      //express 서버 객체
 
app.set('port'3000);
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
///users 를 붙이게 되면 특정 경로를 지정하는 것으로 특정 기능을 수행할떄 사용한다
//localhost:3000/users
//example , get 방식
//localhost:3000/users?name=tesstsssssss
 
app.use(
    function (req, res, next) {
        console.log('middle wared was called : first');
        //res.redirect('http://google.co.kr');
 
        //req 여러 정보를 얻어 올 수 있는데 그중
        //요청받은 request 정보중에서 User-Agent 정볼를 따로 분리하여 갖어올 수 있다
        var userAgent = req.header('User-Agent');
 
        //요청파라미터는 get 방식인 req.query 에 들어오게 된다
        //post 방식은 body로 들어오게된다
        //name 은 정해져있는 명칭
        var paramName = req.query.name;
 
        //응답 보내기
        res.send('<h3>response from server!!!!!!!!!!!! : ' + userAgent + '</h3>' + paramName);
 
    }
);
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
 
);
 






결과 : 



http://localhost:3000/game0.jpg



인사이드 inside








post 방식

npm install body-parser --save 이 모듈을 설치해야한다



위으 소스에서 세군대 정도만 변경 또는 추가가 되는데 주의할 점은 일반 브라우저에서는 post 방식으로 보내면 안된다

주소 뒤에 ?name='sdf'  추가 하는 방식은 get 방식이기 때문에 이렇게 보내면 안되고

body 라는 곳에 추가를 해야 하는데 이것은 postman 이라는 툴을 사용해서 post 방식으로 보다 쉽게 보내는 방식이 존재한다


postman 을 설치하고 다음 처럼 입력하면 결과를 볼 수 있다

postman 으로 param 을 선택한다음 name, 과 출력할 값을 넣어주면 기존 get 방식과 거의 동일한 실행흐름이 진행된다는 것을 알 수 있다




소스코드 :


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
var express = require('express');
var http = require('http');
var serveStatic = require('serve-static');      //특정 폴더의 파일들을 특정 패스로 접근할 수 있도록 열어주는 역할
var path = require('path');
 
var app = express();      //express 서버 객체
//post 방식 일경우 begin
var bodyParser_post = require('body-parser');       //post 방식 파서
//post 방식 일경우 end
app.set('port'3000);
 
//join은 __dirname : 현재 .js 파일의 path 와 public 을 합친다
//이렇게 경로를 세팅하면 public 폴더 안에 있는것을 곧바로 쓸 수 있게된다
app.use(serveStatic(path.join(__dirname, 'public')));
 
 
//post 방식 일경우 begin
//post 의 방식은 url 에 추가하는 방식이 아니고 body 라는 곳에 추가하여 전송하는 방식
app.use(bodyParser_post.urlencoded({ extended:false }));            // post 방식 세팅
app.use(bodyParser_post.json());                                     // json 사용 하는 경우의 세팅
//post 방식 일경우 end
 
 
///users 를 붙이게 되면 특정 경로를 지정하는 것으로 특정 기능을 수행할떄 사용한다
//localhost:3000/users
//example , get 방식
//localhost:3000/users?name=tesstsssssss
 
app.use(
    function (req, res, next) {
        console.log('middle wared was called : first');
        //res.redirect('http://google.co.kr');
 
        //req 여러 정보를 얻어 올 수 있는데 그중
        //요청받은 request 정보중에서 User-Agent 정볼를 따로 분리하여 갖어올 수 있다
        var userAgent = req.header('User-Agent');
 
        //요청파라미터는 get 방식인 req.query 에 들어오게 된다
        //post 방식은 body로 들어오게된다
        //name 은 정해져있는 명칭
        //var paramName = req.query.name;           //get 방식
 
//post 방식 일경우 begin
        var paramName = req.body.name;           //post 방식 //post 방식 일경우 end
 
        //post, get 방식 동시에 되는 것 얻기, || 연산자는 둘중에 null 또는 undefined 가 아닌 것을 리턴한다
        //var paramName = req.query.name || req.body.name;
 
        //응답 보내기
        res.send('<h3>response from server!!7!! : ' + userAgent + '[' + paramName  + '] </h3>');
 
    }
);
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
 
);
 




Postman 으로 입력하는 모습


  1. Post 옆에 url은 localhost:3000 까지만 입력하고 옆에 Params 를 눌러 파라미터를 입력한다
  2. Body 클릭
  3. "x-www-form-urlencoded" 를 클릭한다음 key, value 에 전송할 데이터를 입력


결과는 이미지 하단에 나타남










반응형
반응형

다른 사이트로 리다이렉트 시킬 수 있다

        res.redirect('http://google.co.kr'); //구글로


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
var express = require('express');
 
var http = require('http');
 
var app = express();      //express 서버 객체
 
app.set('port'3000);
 
app.use(
    function (req, res, next) {
        console.log('middle wared was called : first');
        res.redirect('http://google.co.kr');
    }
);
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
 
);
 






다음은 



localhost:3000/users?name=tesstsssssss  를 브라우저 입력하면

결과로 tesstsssssss  를 볼 수 있는 예제이다

var userAgent = req.header('User-Agent');  이 구문으로 요청 req 중에서 User-Agent 정보를 볼 수 있다



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
41
42
43
44
45
46
var express = require('express');
 
var http = require('http');
 
var app = express();      //express 서버 객체
 
app.set('port'3000);
 
 
 
///users 를 붙이게 되면 특정 경로를 지정하는 것으로 특정 기능을 수행할떄 사용한다
//localhost:3000/users
//example , get 방식
//localhost:3000/users?key=state
//localhost:3000/users?name=tesstsssssss
 
app.use(
    function (req, res, next) {
        console.log('middle wared was called : first');
        //res.redirect('http://google.co.kr');
 
        //req 여러 정보를 얻어 올 수 있는데 그중
        //요청받은 request 정보중에서 User-Agent 정볼를 따로 분리하여 갖어올 수 있다
        var userAgent = req.header('User-Agent');
 
        //요청파라미터는 get 방식인 req.query 에 들어오게 된다
        //post 방식은 body로 들어오게된다
        //name 은 정해져있는 명칭
        var paramName = req.query.name;
 
        //응답 보내기
        res.send('<h3>response from server!!!!!!!!!!!! : ' + userAgent + '</h3>' + paramName);
 
    }
);
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
 
);
 






결과 화면 :


User-Agent 정보와

파라미터 중 name 에 대한 결과를 보여준다





반응형
반응형


Express 를 통해서 미들웨어를 등록(use(...)) 할 수 있고 등록된 미들웨어는 

요청이 오게 되면 그 요청에 반응하여 동작 하게 되며 순서는 등록한 순서대로 미들웨어가 실행 되게 된다

    //http 에서 제공되는 모듈에서 express 에 필요한것이 추가된 것을 파라미터 req, res 로 전달된다
    //req : request 객체
    //res : response 객체
    function (req, res, next) {


이때 다음 미들웨어를 호출 할때는 next() 함수로 다음 미들웨어를 호출하는데 

req, res 객체가 다음 미들웨어 인자로 넘어가게 된다






또한 클라이언트로 응답을 보낼때 보낼 내용을 객체로 보낼 수도 있으며

json 형태로 변환하거나 문서 자체를 json 타입으로 보낼 수도 있으며

보내는 방식 또한 여러가지 방식으로 보낼 수 있다


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
var express = require('express');
 
var http = require('http');
 
var app = express();      //express 서버 객체
 
//app 에 임의의 문자열인 port 로 속성을 세팅함
//환경 변수에 PORT 를 설정했다면 그것을 포트로 그렇지 않으면 3000 으로 포트 번호를 express 에 세팅함
app.set('port'3000);
//속성을 set 으로 설정하고 get 으로 가져올 수 있다
 
//미들웨어 등록(미들웨어 == 함수)
app.use(
    //http 에서 제공되는 모듈에서 express 에 필요한것이 추가된 것을 파라미터 req, res 로 전달된다
    //req : request 객체
    //res : response 객체
    function (req, res, next) {
        console.log('middle wared was called : first');
 
        //객체에 멤버 추가
        req.user = 'to next Middleware';
 
        next();       //next를 실행하면 그다음 미들웨어가 받아서 처리하게 됨
        //이때 req, res 를 넘겨준다
 
    }
);
 
 
//그다음 미들웨어 등록
//첫번째 미들웨어로부터 req, res 를 받게 받아서 처리하게 된다
//첫번째 미들웨어의 것을 두번째 미들웨어서 받아서 처리하게 된다
app.use(
    function (req, res, next)
    {
        console.log('middle wared was called : second');
 
        //[1 번째 방법]
        //이때 응답을 보낸다
        /*
        res.writeHead(200,  //정상 응답
            { "Content-Type": "text/html;characterset=utf-8" }    //문서를 html 형과 해당 문자 캐릭터 셋으로 보냅
        );
        res.end('<h1> response from server first </h1>' + req.user);      // 전송함
        */
 
        //[2 번째 방법]
        var val = { 'key'333 };
        //writeHead, end 를 한번에 send, 그리고 객체를 보낼 수가 있음
        //객체를 넘길대는 json 포멧으로 넘어감
        /*
        res.send(val);
        */
 
 
        //[3 번째 방법]
        //val 객체를 json 문자로 바꿔 전송 가능
        /*
        var jsonDatas = JSON.stringify(val);
        res.send(jsonDatas);
        */
 
 
        //[4 번째 방법]
        var jsonDatas = JSON.stringify(val);
        //문서 자체를 json 으로 보내는 방법
        res.writeHead(200,  //정상 응답
            { "Content-Type""application/json;characterset=utf-8" }    //문서를 json 타입으로 보냄
        );
        res.write(jsonDatas);
        res.end();      // 전송함
    }
);
 
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
 
);
 
 





반응형
반응형


Express 로 서버 띄우기




미들웨어 : 구조 내에서 중간 처리를 위한 함수
1)  express 프레임워크에서 사용할 수 있는 중간 처리 목적으로 사용
2) 요청에 대한 응답을 완수하기 전까지 중간중간 다양한 일을 처리할 수 있음
3) 미들웨어 함수 생명주기 : request, response 응답을 주기로 종료 
4) 미들웨어 함수 우선순위 : 먼저 로드되는 미들웨어 함수가 먼저 실행됨(코드 순서 중요)





cmd 를 통해 아래 명령줄을 실행해 설치

npm install express --save


이때 외장모듈과 express 이름과 충돌이 되면 설치가 안될 수 있으니 이름만들때 주의




http 로 서버 실행시키도 대기 시키는 것과 유사한데 다만 express 를 넘겨줘서 


express의 기능들을 사용 할수 있도록 넘겨준다는 점에서 약간 다르다


//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);




아래 구문을 실행한 다음


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var express = require('express');
 
var http = require('http');
 
var app = express();      //express 서버 객체
 
//app 에 임의의 문자열인 port 로 속성을 세팅함
//환경 변수에 PORT 를 설정했다면 그것을 포트로 그렇지 않으면 3000 으로 포트 번호를 express 에 세팅함
app.set('port'3000);
//속성을 set 으로 설정하고 get 으로 가져올 수 있다
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
 
 
 
appServer.listen(app.get('port'),
    function ()
    {
        console.log('express 웹서버 실행' + app.get('port'));
    }
    
);
 




브라우저에서 localhost:3000  를 입력했을때의 결과화면





req, res : 에 대한 설명

Request(req

The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on. In this documentation and by convention, the object is always referred to as req (and the HTTP response is res) but its actual name is determined by the parameters to the callback function in which you’re working.

For example:

app.get('/user/:id', function(req, res) {
  res.send('user ' + req.params.id);
});

But you could just as well have:

app.get('/user/:id', function(request, response) {
  response.send('user ' + request.params.id);
});

The req object is an enhanced version of Node’s own request object and supports all built-in fields and methods.






Response(res)

The res object represents the HTTP response that an Express app sends when it gets an HTTP request.

In this documentation and by convention, the object is always referred to as res (and the HTTP request is req) but its actual name is determined by the parameters to the callback function in which you’re working.

For example:

app.get('/user/:id', function(req, res){
  res.send('user ' + req.params.id);
});

But you could just as well have:

app.get('/user/:id', function(request, response){
  response.send('user ' + request.params.id);
});









Express 와 미들웨어



express 로 실행 되는 흐름


미들웨어 : 클라이언트로부터 요청이 오면 요청을 가로채 처리하는 것을 말함


 즉 여러개의 미들웨어가 있을 수 있음 다음 미들웨어는 next()를 실행해야 다음 미들웨어를 받을 수 있음


미들웨어 등록은 use() 로 설정한다



요청패스 URL 에 따라 다른 함수가 실행되게 만들려면 라우터를 통해서 처리한다


처리 방식은 / 도는  /users 로 요청하여 다른 함수가 실행 될 수 있도록 만들 수 있다







실행 흐름

  1. 웹서버를 실행시킨다

  2. 클라이언트에서 3000 포트로 요청을 한다

  3. 서버의 미들웨어가 요청을 받아 미들웨어의 함수로 처리한다( 클라이언트로 바로 응답을 보낼 수도 있다)

  4. 클라이언트는 요청의한 결과를 화면에 뿌려준다



Express + middleware 로 클라이언트 요청에 의한 함수 실행 시키기


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
41
42
var express = require('express');
 
var http = require('http');
 
var app = express();      //express 서버 객체
 
//app 에 임의의 문자열인 port 로 속성을 세팅함
//환경 변수에 PORT 를 설정했다면 그것을 포트로 그렇지 않으면 3000 으로 포트 번호를 express 에 세팅함
app.set('port'3000);
//속성을 set 으로 설정하고 get 으로 가져올 수 있다
 
//미들웨어 등록(미들웨어 == 함수)
app.use(
    //http 에서 제공되는 모듈에서 express 에 필요한것이 추가된 것을 파라미터 req, res 로 전달된다
    //req : request 객체
    //res : response 객체
    function (req, res, next)
    {
        console.log('middle wared was called first');
 
        //바로 응답 보내기 처리
        res.writeHead(200,  //정상 응답
            { "Content-Type""text/html;characterset=utf-8" }    //문서를 html 형과 해당 문자 캐릭터 셋으로 보냅
        );
        res.end('<h1> response from server first </h1>');      // 전송함
 
    }
);
 
 
 
//웹서버를 app 기반으로 생성
var appServer = http.createServer(app);
appServer.listen(app.get('port'),
    function () {
        console.log('express 웹서버 실행' + app.get('port'));
    }
 
);
 
 
 






서버쪽 실행화면 및 대기






브라우저에서 요청 시도 결과


아래 화면은 클라이언트에서 서버로 요청을 시도한 화면이다 (F12 -> 네트워크 에서 확인)






브라우저에서 응답 헤더를 보면 미들웨어에서 클라이언트로 보내줬던 문서 형식 "Content-Type""text/html;characterset=utf-8" 


이 응답헤더에 있다는 것을 알 수 있다


즉 클라이언트이 요청을 미들웨어가 가로채 대신 처리한다








미들웨어 여러개 처리 흐름













미들웨어 사용

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크이며, Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시됩니다.

미들웨어 함수는 다음과 같은 태스크를 수행할 수 있습니다.

  • 모든 코드를 실행.
  • 요청 및 응답 오브젝트에 대한 변경을 실행.
  • 요청-응답 주기를 종료.
  • 스택 내의 그 다음 미들웨어 함수를 호출.

현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다.

Express 애플리케이션은 다음과 같은 유형의 미들웨어를 사용할 수 있습니다.

애플리케이션 레벨 및 라우터 레벨 미들웨어는 선택적인 마운트 경로를 통해 로드할 수 있습니다. 일련의 미들웨어 함수를 함께 로드할 수도 있으며, 이를 통해 하나의 마운트 위치에 미들웨어 시스템의 하위 스택을 작성할 수 있습니다.

애플리케이션 레벨 미들웨어

app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드하십시오. 이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메소드입니다.

다음 예에는 마운트 경로가 없는 미들웨어 함수가 표시되어 있습니다. 이 함수는 앱이 요청을 수신할 때마다 실행됩니다.


var app = express();

app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

다음 예에는 /user/:id 경로에 마운트되는 미들웨어 함수가 표시되어 있습니다. 이 함수는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대해 실행됩니다.


app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

다음 예에는 라우트 및 해당 라우트의 핸들러 함수(미들웨어 시스템)이 표시되어 있습니다. 이 함수는 /user/:id 경로에 대한 GET 요청을 처리합니다.


app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

아래에는 하나의 마운트 경로를 통해 일련의 미들웨어 함수를 하나의 마운트 위치에 로드하는 예가 표시되어 있습니다. 이 예는 /user/:id 경로에 대한 모든 유형의 HTTP 요청에 대한 요청 정보를 인쇄하는 미들웨어 하위 스택을 나타냅니다.


app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

라우트 핸들러를 이용하면 하나의 경로에 대해 여러 라우트를 정의할 수 있습니다. 아래의 예에서는 /user/:id 경로에 대한 GET 요청에 대해 2개의 라우트를 정의합니다. 두 번째 라우트는 어떠한 문제도 발생키지 않지만, 첫 번째 라우트가 요청-응답 주기를 종료시키므로 두 번째 라우트는 절대로 호출되지 않습니다.

다음 예에는 /user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.


app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

라우터 미들웨어 스택의 나머지 미들웨어 함수들을 건너뛰려면 next('route')를 호출하여 제어를 그 다음 라우트로 전달하십시오. 참고next('route')는 app.METHOD() 또는 router.METHOD() 함수를 이용해 로드된 미들웨어 함수에서만 작동합니다.

*use 함수에서 next('route')를 쓰면 다음 use 문으로 가는 것이 아니고 next()와 동일하게 다음 스택 미들웨어 함수로 간다


(app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드하십시오. 이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메소드입니다.)

다음 예에는 /user/:id 경로에 대한 GET 요청을 처리하는 미들웨어 하위 스택이 표시되어 있습니다.


app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id == 0) next('route'); // 이게 호출되면 다음 app.get('user/:id, 쪽으로 뛴다
  // otherwise pass the control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {    // 
  res.render('special');
});

라우터 레벨 미들웨어

라우터 레벨 미들웨어는 express.Router() 인스턴스에 바인드된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일한 방식으로 작동합니다.


var router = express.Router();

router.use() 및 router.METHOD() 함수를 사용하여 라우터 레벨 미들웨어를 로드하십시오.

다음 예의 코드는 위에서 애플리케이션 레벨 미들웨어에 대해 표시된 미들웨어 시스템을 라우터 레벨 미들웨어를 사용하여 복제합니다.


var app = express();
var router = express.Router();

// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next router
  if (req.params.id == 0) next('route');
  // otherwise pass control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// mount the router on the app
app.use('/', router);

오류 처리 미들웨어

오류 처리 미들웨어에는 항상 4개의 인수가 필요합니다. 어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개의 인수를 제공해야 합니다. next 오브젝트를 사용할 필요는 없지만, 시그니처를 유지하기 위해 해당 오브젝트를 지정해야 합니다. 그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류 처리에 실패하게 됩니다.

다른 미들웨어 함수와 동일반 방법으로 다음과 같이 오류 처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 구체적으로 말하면 (err, req, res, next) 시그니처를 갖는다는 점이 다릅니다.


app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

오류 처리 미들웨어에 대한 상세 정보는 오류 처리를 참조하십시오.

기본 제공 미들웨어

4.x 버전 이후의 Express는 더 이상 Connect에 종속되지 않습니다. express.static의 실행을 제외하면, 이전에 Express와 함께 포함되었던 모든 미들웨어 함수는 이제 별도의 모듈에 포함되어 있습니다. 미들웨어 함수 목록을 확인하십시오.

express.static(root, [options])

Express의 유일한 기본 제공 미들웨어 함수는 express.static입니다. 이 함수는 serve-static을 기반으로 하며, Express 애플리케이션의 정적 자산을 제공하는 역할을 합니다.

root 인수는 정적 자산의 제공을 시작하는 위치가 되는 루트 디렉토리를 지정합니다.

선택사항인 options 오브젝트는 다음과 같은 특성을 가질 수 있습니다.

특성설명유형기본값
dotfilesdotfile을 제공하기 위한 옵션입니다. 사용 가능한 값은 “allow”, “deny” 및 “ignore”입니다.문자열“ignore”
etagetag의 생성을 사용 또는 사용 안함으로 설정합니다.부울true
extensions파일 확장자 폴백을 설정합니다.배열[]
index디렉토리 인덱스 파일을 전송합니다. 디렉토리 인덱스 작성을 사용하지 않으려면 false를 설정하십시오.혼합“index.html”
lastModifiedOS에서 해당 파일이 마지막으로 수정된 날짜를 Last-Modified 헤더에 설정합니다. 사용 가능한 값은 true 또는 false입니다.부울true
maxAge밀리초 또는 ms 형식의 문자열로 Cache-Control 헤더의 max-age 특성을 설정합니다.숫자0
redirect경로 이름이 디렉토리인 경우 후미부의 “/”로 경로를 재지정합니다.부울true
setHeaders파일을 제공하도록 HTTP 헤더를 설정하기 위한 함수입니다.함수 

아래에는 상세한 옵션 오브젝트와 함께 express.static 미들웨어 함수를 사용하는 예가 표시되어 있습니다.


var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));

다음과 같이, 하나의 앱은 2개 이상의 정적 디렉토리를 가질 수 있습니다.


app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

serve-static 함수 및 그 옵션에 대한 자세한 정보는 serve-static 문서를 참조하십시오.

써드파티 미들웨어

Express 앱에 기능을 추가하려면 써드파티 미들웨어를 사용하십시오.

필요한 기능을 위한 Node.js 모듈을 설치한 후, 애플리케이션 레벨 또는 라우터 레벨에서 해당 모듈을 앱에 로드하십시오.

다음 예는 쿠키 구문 분석 미들웨어 함수인 cookie-parser의 설치 및 로드를 나타냅니다.


$ npm install cookie-parser


var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());

Express와 함께 일반적으로 사용되고 있는 써드파티 미들웨어 함수의 일부 목록을 확인하려면 써드파티 미들웨어를 참조하십시오.


ref : http://expressjs.com/ko/guide/using-middleware.html

반응형
반응형

이미지를 프로젝트에 넣고 전송하기


html 문자 보내기 예제는 여기를 참고 (http://3dmpengines.tistory.com/1860)





1. 이미지는 파일시스템 fs 로 로딩하여 보낸다


2. 이미지가 로딩이 완료 된 이후에 전송처리를 함



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
41
42
43
44
45
46
47
48
49
var http = require('http');                 //웹서버 기능을 담당하기 위한 모듈
var server = http.createServer();           //서버 객체를 얻어옴
var fs = require('fs');
 
 
//서버에서 대기하기 위한 처리
var localHost = 'localhost';
var port = 3000;
var backLog = 50000;    //  동시에 접속 할 수 있는 클라이언트 수
server.listen(port, localHost, backLog,
    function () {
        console.log('웹서버 실행됨 :' + localHost + ':' + + port);
    }
);
 
 
server.on('connection',
    function (socket) {
        console.log('클라이언트가 접속');
    }
);
 
 
server.on('request',
    function (req, res) {
        console.log('클라이언트가 요총이 들어옴');
        //consloe.dir(req);
 
 
        var filename = 'park.jpg';
        fs.readFile(filename, //파일 읽기
            function (err, data)
            {
                //http의 헤더정보를 클라이언트쪽으로 출력
                //image/jpg : jpg 이미지 파일을 전송한다
                //write 로 보낼 내용을 입력
                res.writeHead(200, { "Context-Type""image/jpg" });//보낼 헤더를 만듬
                res.write(data);   //본문을 만들고
                res.end();  //클라이언트에게 응답을 전송한다
 
            }
        );
 
    }
);
 
 
 
 




결과 화면 : 브라우저 상단에 localhost:3000 을 입력하면 결과 이미지가 뜬것을 볼 수 있다



반응형
반응형






nodejs 로 아래 코드를 실행 시킨다음 서버를 돌리고 


브라우저를 띄워 주소창에 http://localhost:3000 입력하면 


receive response from server

이란 응답을 받을 수가 있다



클라이언트에서 접속하면 서버 내부적으로 socket 이 만들어지는데 이것을 


call-back 함수로 넘겨받게 되어있다


server.on('connection',
    function (socket) {
        console.log('클라이언트가 접속');
    }
);
 







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
41
var http = require('http');                 //웹서버 기능을 담당하기 위한 모듈
var server = http.createServer();           //서버 객체를 얻어옴
 
 
//서버에서 대기하기 위한 처리
var localHost = 'localhost';
var port = 3000;
var backLog = 50000;    //  동시에 접속 할 수 있는 클라이언트 수
server.listen(port, localHost, backLog,
    function ()
    {
        console.log('웹서버 실행됨 :' + localHost +':'+ + port);
    }
);
 
 
server.on('connection',
    function (socket) {
        console.log('클라이언트가 접속');
    }
);
 
 
server.on('request',
    function (req, res) {
        console.log('클라이언트가 요총이 들어옴');
        //consloe.dir(req);
 
        //http의 헤더정보를 클라이언트쪽으로 출력
        //text/html : html 파일을 전송한다
        //characterset : 문자열 셋은 utf-8
        //write 로 보낼 내용을 입력
        res.writeHead(200, { "Context-Type""text/html;characterset=utf-8" });//보낼 헤더를 만듬
        res.write('<h1> receive response from server </h1>');   //본문을 만들고
        res.end();  //클라이언트에게 응답을 전송한다
    }
);
 
 
 
 





결과 화면










반응형
반응형


옵션 중에 로그 파일이름에 날짜를 붙일 수도 있다


1
2
3
var winston = require('winston');   //로그파일
var winstonDaily = require('winston-daily-rotate-file');  //매일 다른 파일 로그를 생성하는 모듈
var moment = require('moment');     //날짜






로그를 남기는건 중요하다.

데이터는 곧 힘이 된다. 사람들이 무엇을 원하는지 알면 그들이 원하는 걸 주고 내가 원하는 걸 받을 수 있다.

데이터를 쌓는데 가장 핵심적인 일 중 하나가 로그를 꼼꼼히 남기는 일이다.

node기반의 logging라이브러리는 다양하지만 꽤 오래전부터 winston이라는 라이브러리가 패권을 잡고있다.

오늘은 winston에 대해 알아보자.

프로젝트를 생성하고 winston을 npm으로 설치한다.

npm install --save winston

임포트해보자.

기본 설정


var winston = require('winston');

var logger = new (winston.Logger)({
    transports: [
      new (winston.transports.Console)()
    ]
  });

그냥 바로 사용해도 되지만 편의상 logger를 만들어서 사용했다.

사실 벌써 끝난거나 다름없다.

logger.debug('디버그 로깅');
logger.info('인포 로깅');
logger.error('에러 로깅');

이런식으로 사용하면 로그가 출력된다.

사실 winston에서 가장 중요한건 transports라고 할 수 있는데, 형식을 정하거나 할 수 있다.

나는 로그를 file로 관리하고, 날짜별로 관리한다면 그것도 가능하다.

우선 타임스탬프부터 찍어보자.

나는 날짜별로 관리하기때문에 시간만 찍어보았다.

타임스탬프 찍기


const winston = require('winston');
const tsFormat = () => (new Date()).toLocaleTimeString();
const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({
      timestamp: tsFormat
    })
  ]
});

logger.info('Hello world');

로그레벨은 기본적으로 5가지가 있다.

{ error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }

때에 맞춰 사용하면 더 식별하기 쉬운 로그가 된다.

커스텀도 가능하지만 그건 다루지 않을거고 Winston 공식문서를 참조하자.

파일로 로그 관리하기


먼저 파일로 로그를 관리하기 위해 파일시스템 라이브러리 fs를 설치한다.

npm install --save fs

풀 코드는 이렇다.

const winston = require('winston');
const fs = require('fs');
const logDir = 'log';

if (!fs.existsSync(logDir)) {
  fs.mkdirSync(logDir);
}

const tsFormat = () => (new Date()).toLocaleTimeString();
const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({
      timestamp: tsFormat,
      level: 'info'
    }),
    new (winston.transports.File)({
      level: 'info',
      filename: `${logDir}/logs.log`,
      timestamp: tsFormat,
      maxsize:1000000,
      maxFiles:5
    })
  ]
});

로그 디렉토리가 없으면 생성하고 그 아래 로그파일들을 적재하는 코드다.

maxSize를 넘어가면 새로운 파일이 생기고, maxFiles만큼만 생성된다.

logs.log 다음은 logs1.log이런식으로 생성된다.

날짜별로 로그 파일관리하기


원래는 winston안에 daily-rotate-file이 함께 있었는데, 어떤이유에서인지 분리되어 나왔다.

winston-daily-rotate-file을 설치하자

npm install --save winston-daily-rotate-file

풀 소스는 이렇다.

var winston = require('winston');
require('date-utils');
const fs = require('fs');
const logDir = 'log';

if (!fs.existsSync(logDir)) {
  fs.mkdirSync(logDir);
}

const tsFormat = () => (new Date()).toLocaleTimeString();

var logger = new (winston.Logger)({
    transports: [
      new (winston.transports.Console)({
       timestamp: tsFormat,
       colorize: true,
       level: 'info'
     }),
      new (require('winston-daily-rotate-file'))({
        level: 'info',
        filename: `${logDir}/-logs.log`,
        timestamp: tsFormat,
        datePattern: 'yyyy-MM-dd',
        prepend: true,
      })
    ]
  });

이렇게하면 2017-03-03-logs.log라는 파일에 오늘치 로그가 쌓이게 된다.

winston으로 로그관리를 간단히 할 수 있게됬다. 



ref : https://isme2n.github.io/devlog/2017/03/03/Winston-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/

반응형

+ Recent posts