Arduino Uno - Arduino M0(SAMD21G) serial 통신

참고: https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/creating-a-new-serial

참고: https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/muxing-it-up


*Uno

rx pin - D2

tx pin - D3


코드


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
#include <SoftwareSerial.h>
 
 
 
SoftwareSerial ser(2,3);
 
 
 
void setup() {
 
  // put your setup code here, to run once:
 
  Serial.begin(9600);
 
  ser.begin(9600);
 
  Serial.println("hi im uno");
 
}
 
 
 
void loop() {
 
  ser.write('c');
 
  while(ser.available() > 0){
 
    char temp = ser.read();
 
    Serial.println(temp);
 
    
 
  }
 
  delay(1000);
 
}
cs



*M0(SAMD21G)

rx pin - 30(PB03)

tx pin - A5(PB02)


코드


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
#include <Arduino.h>
 
#include "wiring_private.h"
 
#include <LiquidCrystal_I2C.h>
 
 
 
Uart SerialX(&sercom5, 30, A5, SERCOM_RX_PAD_1, UART_TX_PAD_0); // rx, tx
 
LiquidCrystal_I2C lcd(0x38204);
 
 
 
void SERCOM5_Handler(){
 
//  SerialUSB.println("irq");
 
  SerialX.IrqHandler();
 
}
 
 
 
void setup() {
 
  lcd.init();
 
  lcd.backlight();
 
  lcd.clear();
 
  SerialUSB.begin(9600);
 
  SerialX.begin(9600);
 
  pinPeripheral(30, PIO_SERCOM_ALT);
 
  pinPeripheral(A5, PIO_SERCOM_ALT);
 
  SerialUSB.println("hi im m0_serialUSB");
 
  lcd.setCursor(0,0);
 
  lcd.print("hi");
 
  
 
}
 
 
 
uint8_t i = 0;
 
 
 
void loop() {
 
//  SerialUSB.println(i);
 
  SerialX.write('a');
 
//  delay(5);
 
  while(SerialX.available()){
 
//    SerialUSB.print(" - > 0x");
 
    lcd.setCursor(0,0);
 
    char temp = SerialX.read();
 
    lcd.print(temp);
 
    SerialUSB.println(temp);
 
  }
 
  delay(1000);
 
}
cs


SerialX라는 이름의 Uart객체를 생성한다. sercom은 5를 사용한다.

Arduino M0(SAMD21G)는 총 6개의 sercom을 가지고 있으며 chip(SAMD21G)만 따로 떼어내서 사용하는 것이 아니라면 각각의 sercom은 용도에 맞게 예약되어 있다.

sercomN의 각각의 자주쓰는(chip만 떼어서 쓴다고하면 다른용도로도 사용할 수 있다는 것을 명심) 용도는 다음과 같다.

출처: https://forum.arduino.cc/index.php?topic=341054.0 에서 MartinL의 글

참고: variant.cpp 

참고: variant.h


sercom0 - Uart Serial (variant.cpp (링크)에서 Serial1 그리고 Serial5으로 선언이 되어있다.)

sercom1 - Uart Serial

sercom2 - Uart Serial

sercom3 - I2C Wire

sercom4 - SPI

sercom5 - EDGB (variant.cpp (링크)에서 Serial로 선언이 되어있다.


M0에서  uart serial 생성에서 기억해야 할 코드는 다음과 같다.


Uart SerialX(&sercom5, 30, A5, SERCOM_RX_PAD_1, UART_TX_PAD_0);


void SERCOM5_Handler(){

SerialX.IrqHandler();

}


SerialX.begin(9600);


pinPeripheral(30, PIO_SERCOM_ALT);

pinPeripheral(A5, PIO_SERCPM_ALT);


각각 설명을 해주면

------------------------------------------------------------------------------------------------------------------------

Uart SerialX(&sercom5, 30, A5, SERCOM_RX_PAD_1, UART_TX_PAD_0);


SerialX라는 이름의 serial 핸들링 객체를 하나 만든다.


파라미터는

Serial( sercomN의 주소값, RX pin number, TX pin number, RX_PAD number, TX_PAD number)

로 구성되어있다.


나는 sercom5를 사용했다.

sercom은 runtime시에 하나만 사용할 수 있다.

------------------------------------------------------------------------------------------------------------------------

RX는 현재 PB03를 사용하고 있다.

TX는 현재 PB02을 사용하고 있다.


windows의 경우 

C:\Users\user\AppData\Local\arduino15\packages\arduino\hardware\samd\1.6.20\variants\arduino_mzero

경로에서 variant.cpp를 보면 코드에서 사용할 수 있는 pin 번호가 적혀 있다.




위의 캡처한 사진을 보면

PB03는 30번 핀을 사용하고 있고


PB02은 A5번 핀을 사용하고 있다.


따라서 2번째 3번째 파라미터는 


 30, A5이 되는 것이다.


Uart SerialX(&sercom5, 30, A5, SERCOM_RX_PAD_1, UART_TX_PAD_0);

------------------------------------------------------------------------------------------------------------------------

3번째 4번째 파라미터는

PAD인데 정확한 의미와 뜻은 잘모른다. mux로 되어있다는 것만 알 수 있다.

그러나 어떻게 사용해야하는지는 알겠다.

SERCOM_RX_PAD_N 형태로 RX_PAD를 설정하고

UART_TX_PAD_N 형태로 TX_PAD 형태로 설정한다.


위의 사진과 SAMD21G의 datasheet(링크)의 23페이지를 살펴보면

PB02과 PB03을 사용하려면 sercom5와 PAD[0] PAD[1]을 사용해야한다.

PB02를 sercom5를 이용해 사용하려면 PAD[0]를 사용해야 한다는 것을 유추할 수 있다.

PB03은 datasheet에서 PAD[1]을 사용하라고 나와있다.



Uart SerialX(&sercom5, 30, A5, SERCOM_RX_PAD_1, UART_TX_PAD_0);

------------------------------------------------------------------------------------------------------------------------


void SERCOM5_Handler(){

SerialX.IrqHandler();

}


는 SerialX의 핸들러를 가져오겟다는 뜻이다. sercom을 사용하기 위해서는 반드시 정의해줘야한다. 함수명이 SERCOMN_Handler 형태로 되어있는 것에 주목하도록하자 우리는 sercom5를 쓰기때문에 SERCOMN_Handler에서 N에 5가 들어갔다.

이 함수는 콜백메소드이다.

------------------------------------------------------------------------------------------------------------------------

SerialX.begin(9600);


따로 설명하지 않겠다.

------------------------------------------------------------------------------------------------------------------------

pinPeripheral(30, PIO_SERCOM_ALT);

pinPeripheral(A5, PIO_SERCPM_ALT);


아까 선택했던 rx pin과 tx pin을 사용해줘야 한다.


pinPeripheral함수를 이용한다.

30번 핀을 PIO_SERCOM_ALT로 사용하겠다는 의미이다.

아까 데이터시트를 보면 D항목의 SERCOM5-ALT항목의 

위의 데이터시트 캡처화면의 PB03와 PB02에서 SERCOM5/PAD[0] 열따라 쭉 올라가보면 SERCOM5-ALT가 보인다.

pinPeripheral의 두번째 파라미터는

PAD가 ALT인지 아닌지에 따라 달라질 수 있다.

여기서는 ALT이므로 PIO_SERCOM_ALT 를 사용한다.

ALT가 아닐 시에는 PIO_SERCOM을 사용한다.


------------------------------------------------------------------------------------------------------------------------


*결과


M0 serial monitor


Uno serial monitor




'개발 tip > Arduino' 카테고리의 다른 글

[아두이노] MAC OS X에서 VS Code 개발 환경 세팅  (0) 2019.01.19

Node.js REST API - environment split


*환경의 분리

서버가 실행되는 모드를 정의해야 한다. 이건 테스트 때문이다. 아직 테스트에 db 붙이지는 않았지만 테스트에 디비를 붙일 것이다. 하지만 테스트 디비를 붙이게 되면 데이터베이스 테스트에서 사용한 데이터들이 쌓이게 된다. 따라서 테스트용 데이터 베이스가 따로 있어야하는데 이것을 위해 서버 환경을 분리할 것이다.


우리는 세가지 모드를 사용할 것이다.

-development

-test

-production

“development” 개발 모드이다. 우리가 지금까지 사용했던 환경이다.

“test” 테스트 환경을 의미한다.

“production” 운영모드이다. 실제로 코드가 서버로 배포되어 동작하는 환경을 의미한다.


이러한 환경 정보는 NODE_ENV라는 환경변수에 설정하여 사용할 수있다. 노드 코드에서는 process.env.NODE_ENV 라는 변수를 통해 접근할 있다.


테스트 환경과 개발환경을 분리하기 위해서 app/config/environment.js파일을 만들자.


environments 라는 변수를 두어 환경 이름에 해당하는 키를 만들었다. 그리고  nodeEnv라는 상수에 노드 환경변수 값을 할당했다. 

노드를 실행하기 전에 “Node_ENV = test”라고 실행하면 이값에 “test”라는 문자열이 들어간. 만약 아무것도 설정하지 않으면 “development”문자열이 들어가게 될것이다.

마지막으로 environments 객체에서 노드 환경변수에 해당하는 부분의 객체를 반환하는 모듈로 만들었다.


*테스트에 데이터베이스 연동하기

environment 모듈을 사용하여 기존코드를 변경해야한다.

environment에는 데이터베이스 접속정보가 있는데 부분은 models.js 수정하면 된다.

아래 코드처럼 말이다.



 environments 모듈은 NODE_ENV 환경변수 값에 따라 각각 다른 데이터베이스로 연결한다.


그럼  api/users/user.spec.js 테스트 코드로 돌아가 보자. 모카에는 before() / after() 함수가 있다.

이것은 테스트가 실행되기 /후에 각각 한번씩 실행되는 함수이다. before() 함수를 이용해 테스트 데이터베이스를 초기화하는 등의 테스트 환경을 만들어 있다. 아래 코드를 한번 보자


it()함수가 실행되기 직전 before() 함수가 먼저 실행된다. before() 함수에서는 데이터베이스를 초기화 수있는 sync({force: true}) 함수를 실행하면 된다.



*sync-database 모듈

한편 app.js에서 sync() 함수를 사용하고 있는데 이것을 테스트에서도 따로 떼어 내기 위해서는 데이터를 싱크할 있는 별도의 모듈로 떼어내는 것이 편리하다.


app/config/environment.js 다시 수정한다.


test mode에서는 sequelize force true이지만 development production에서는 false이어야만 한다.


 bin/sync-databse.js 파일로 만들어 보자


모듈은 데이터베이스를 싱크하는 매우 간단한 코드이다.

실행 모드가 test이냐 아니냐에 따라서force 값이 달라질것이다.


*www.js

이참에 서버 구동하는 모듈도 별도로 만들어 보자 bin/www.js 파일을 만든다.



서버 구동로직을 옮겨왔으니 app.js에서도 이부분을 제거해야하한다. app.js 남은 부분은 아래와 같다.


이제는 app.js 아니라 bin/www.js 파일로 서버를 구동할 있다. package.json start 스크립트도 변경해야한다.



*테스트 데이터 구성

이제 테스트 코드의 before() 함수에서 데이터베이스 싱크 모듈을 불러와 실행해 보자

user.spec.js  describe()함수의 before()


이제 실제 데이터베이스에 샘플데이터를 넣고 “GET /users” 테스트 해보자 먼저 before()함수에서 데이터베이스에 데이터 3개를 넣는 코드를 작성한다.


before()함수는 여러개 실행할 있다. 전부 it() 함수가 호출되기 전에 실행이 완료 된다.


두번째 before() 함수에서는 데이터베이스에 users  테이블에 있는 유저를 추가하는 역할을 한다.

sequelize 모델에는 create() 말고도 bulkCreate() 함수가 있다. create()  함수가 하나의 row 생성한다면 blukCreate()  함수는 여러개 데이터를 배열로 받아 여러개 row 생성하는 함수이다. 이렇게 샘플데이터를 테이블에 넣은 it()으로 api테스트를 진행 있다.


*테스트 데이터 삭제

마지막으로 after() 함수를 이용해 데이터베이스를 초기화 한다. 유저 데이터를 넣었기 때문에 다시 삭제하는 것이다. 여기서는 간단히 drop 데이터베이스 싱크를 돌리자.


after(‘clear up database’, (done) => {

models.User.drop();

syncDatabase().then( () => done());

});


*테스트 코드 실행

마지막으로 테스트를 실행해야한다. 이번에는 NODE_ENV 환경변수를 설정해 주어야 한다. 우리는 테스트 데이터베이스와 개발 데이터베이스를 분리했으니까 테스트시에는 테스트 데이터베이스로 접속해야한다.

테스트 실행시  “NODE_ENV=test” 추가해주면 된다.

package.json


그리고 명령문에서 npm test 테스트를 진행한다.


$ npm test


*나머지 테스트코드에서도 데이터베이스를 연결해보자

  • Create(POST)


  • Destroy(DELETE)

*폴더정리

다시 한번리펙토링을 해야할 같다. 리펙토링은 많이 할수록 좋음!

bin이라는 폴더를 만들게 되면서 서버 어플리케이션 코드와 섞이게 되었다.


bin폴더에는 두개의 파일이 있는데 데이터베이스를 싱크하는 sync-database.js 서버를 실제로 구동하는 www.js 있다. 엄밀히 말하는 어플리케이션 로직은 아닌셈이다.


한편 api 폴더에는 라우팅 로직들과 models.js 정의된 모델링 코드들은 어플리케이션으로 분리할 있다.


아래와 같이 파일들을 옮기자

/app: 서버 기능

/api: api로직을 담당

/config: 서버가 구동하기 위한 환경 변수 정의 (상수)

/models: 데이터베이스 모델링

/bin: 서버 구동을 위한 코드

/www.js: 서버 구동

/sync-database: db 싱크

파일에서 사용하는 모듈의 상대 경로도 변경하는 것을 잊지말자.

코드는 https://github.com/kiryun/NodeJS_REST-API_tutorial/commits/master 에서 볼 수 있다.



Node.js REST API DB Sync


*데이터베이스 연동

일반적인 백엔드 구조


지금 까지 했던 작업을 Client, Server, Database 중에 Server부분을 만들었음. Client에서 요청하는 것은 curl 사용하거나 모카 테스트로 진행했음.


남은 것은 가장 오른쪽에 있는 database 부분이다. 백에드에서 database 직접 만드는 것은 아님.

다양한 데이터베이스 프로그램이 있는데 그중 MySQL 사용할 것이다. MySQL 우리 컴퓨터에 개발용으로 설치하고 Server에서는 단지 데이터베이스에 연결하는 것이다.


*MySQL

설치


$ sudo apt-get install mysql-server


mysql 설치한 서버를 구동해야 한다. mysql 서버를 데몬이라고도 하는데 리눅스에서 말하는 데몬 혹은 서비스와 같은 것이다. 서버에서 백그라운드에서 실행중인 프로세스를 데몬이라고 한다. 로컬에서도 개발용으로 MySQL데몬을 실행해야 한다.


$ mysql.server start

반대로 종료할때는 

$ mysql.server stop


우분투에서는


이렇게 한다.


접속명령어는


$ mysql -u root -h localhost -p


mysql 명령어의 -u옵션은 서버 접속 계정을 넣을 사용하는데 기본 값인 root 사용했다. -h 옵션은 서버 접속주소를 설정하는 옵션인데 우리는 로컬 컴퓨터에 구동중인 mysql서버에 접속하므로 localhost 사용했다. 마지막 -p 옵션을 비밀번호를 입력하기 위한 기능이다. 명령어를 실행하면 비밀번호를 입력하도록하는데 root 입력하고 접속한다. 




DB목록 조회


DB 생성


DB선택



여기서 부터는 mysql명령어를 사용하지 않아도 된다.


*Sequelize

노드 코드에서 mysql 접속해서 이런 저런 쿼리문을 실행할 때는 노드 코드로 만든 mysql 패키지가 ㅣㄹ요하다. 그것이 ode-mysql이다. 이것을 우리 프로젝트에 추가해서 쿼리문을 직접 실행시킬수 있음.

이를 ORM(object relational mapping)이라고 하는데, ORM 사용하면 쿼리문을 모르더라도 자신이 사용하는 프로그래밍 언어로 데이터베이스에 명령을 내릴 있다. 노드에는 sequelize라는 orm라이브러리가 있음.


$ npm i sequelize —save


*Model

서버에서 하나의 자원을 정의할 그것을 모델이라고 한다. 우리는 지금까지 User라는 자원을 사용했다. 이것이 User모델이다. 모델은 데이터베이스의 테이블과 1:1 매칭된다고 보면 된다. 그러면 데이터베이스에 User테이블이 있어야 . User테이블을 만들기 위해, 다시말하면 User모델을 만들기 위해 Sequelize 도움을 받아야 한다.


모델을 만드는 역할을 하는 models.js 파일을 만들자.




 sequelize모듈을 가져와 Sequelize상수에 할당했다. 그다음엔 Sequelize객체를 하나 만들어 sequelize 상수에 할당했다.

Sequelize객체를 만들 때는 3개의 파라미터가 필요한데 데이터베이스 이름, 접속 계정명, 비밀번호 순이다. 순서대로 문자열로 파라미터를 넘겨주면 sequelize객체를 얻을 있다.

객체가 제공하는 메소드중 define()함수를 이용해 모델을 만들 있음.



define() 함수의 첫번째 파라미터가 데이터베이스에 만들어질 테이블 이름이다.


다음에는 테이블의 세부사항을 객체 형식으로 정의해야한다. 이전에 유저 객체에는 id name 있었는데 둘을 여기서 정의하면 된다.

name: Sequelize.STRING name컬럼을 정의하는 코드이다.Sequelize.STRING 상수를 이용해 name값이 문자열임을 정의했다.

그럼 id 어디에?

-> sequelize 기본적으로 id 만들어 준다.


게다가 createdAt, updatedAt 이라는 칼럼도 자동으로 만들어 준다. 컬럼들의 역할은

테이블안에 데이터를 row라고 하는데 row 생성될 마다 createAt 시간정보가 기록된다. 그리고 row 변경될 때마다 updatedAt컬럼값이 변경된다.


마지막으로 module.export해준다.



*DB Sync

sequelize객체가 제공하는 메소드 중에는 모델을 정의하는 define()외에도  sync()라는 메소드가 있다. 함수를 실행하면 sequelize객체에 연결된 데이터베이스에 우리가 정의한 모델들을 테이블로 생성하는 기능이다.

이러한 작업은 서버가 구동될 한번만 호출되면 된다. 그래서 서버의 시작점인 app.js 만들도록 한다.


  • 다음과 같은 오류가 출력되는 경우



$ npm i install mysql2 —save

  • 다음과 같은 오류가 출력되는 경우


$ sudo mysql -u root -h localhost -p
> use mysql
> update user set authentication_string=password(‘’), plugin=‘mysql_native_password’ where user=‘root’;


성공


app.listen()으로 서버가 구동된 다음에 콜백함수가 동작하면 ‘Example app listening on port 3000!’라는 메시지를 콘솔에 출력하게 된다.

그리고 나서 방금 만들었던 models 모듈의 sequelize객체를 가져와서 sync() 함수를 실행한다. 데이터베이스에 테이블을 만들기 위해서다.


그런데 sync()함수에 {force: true}옵션을 넘겨주었다. force라는 속성에는 불리언 데이터를 설정할 있는데 true 경우 sync()함수가 실행되면 무조건 테이블을 새로 만드는 옵션이다. 반대로 force 값이 false 경우에는 데이터 베이스에 테이블이 있을 경우 다시 만들지 않는 기능이다. 지금은 개발용이기 때문에 {force: true} 설정했지만 실제 운영중인 서버라면 반드시 {force: false} 옵션으로 실행해야 한다.



방금 만들었던 users 테이블이 생겼다. 그런데 define()함수로 테이블명을 user 했는데 위의 사진은 users 되어있다. sequelize에서 자동으로 변환해서 만들어 준것이다.


테이블 정보 확인


id 값은 sequelize 자동으로 생성한 컬럼이다. 프라이머리 키로 설정되어 row 생성될 마다 자동으로 id값이 증가한다.

name 우리가 정의한 컬럼이다. sequelize.STRING 값으로 정의했는데 varchar(255) 설정된 것을 확인 해볼 있다.

그리고 createdAt updateAt sequelize 자동으로 만들어준 컬럼이다. datetime형식으로 되어있음.


*컨트롤러에 데이터베이스 연동


Sequelize 로컬에 구동중인 데이터베이스와 api서버를 연결했다. 그리고 우리가 모델링한 테이블까지 데이터베이스 안에 만들었음.


이제는 테이블에 데이터를 넣거나 조회하거나 삭제 그리고 업데이트하는 작업을 차례이다. 이것은 CRUD기능이라고 한다. create, read, update, delete  기능을 말하는 . api 기능이 데이터베이스의 데이터를 crud하는 것이라 보면 된다.

지금까지는 데이터를 users  배열에 넣고 개발했는데 이제는 데이터베이스를 이용한다.


*create

먼저 user컨트롤러에서 models 가져온다.

/api/users/user.controller.js



테이블이 아직은 비어있기 때문에 먼저 테이블에 넣는 create() 사용해보자


name파라미터를 검증하는 부분까지는 이전코드와 같다. name값이 확보되었다면 models모듈을 이용해 테이블에 데이터를 추가하는 것이 남았음. 

models.User객체는 crud 해당하는 메소드 들을 제공하는데 그중 create() 메소드는 테이블에 데이터를 추가하는 기능을 한다. 파라미터로 넣은 데이터를 객체 형식으로 넘겨준다. 

name컬럼에 name상수값을 넣어줬다.

그리고 then함수가 동작하면 콜백함수의 user파라미터로 테이블에 생성된 row 나온다. 이것을 요청한 클라이언트에 그대로 전달해 주면 된다.


*read

데이터를 조회하는  api 컨트롤러는 index(), show() 메소드가 있다.


index() 모든 사용자 목록을 조회하는 것이기 때문에 테이블 전체 데이터를 불러와야한다.

models.User 객체의  findAll() 메소드를 사용하면 테이블의 전체 데이터를 불러올 있다.




show() 특정사용자를 id 조회하는 역할을 한다. models.User 모델은 findOne() 함수로 조건을 줘서 데이터를 조회한다. where 라는 부분에 id컬럼의 조건값을 설정하여 넘겨준다. 함수가 실행되면 콜백함수의 파라미터로 조회한 user객체가 응답된다. 만약 user값이 비어있다면 테이블에서 id 해당하는 데이터를 찾지 못한것. 이경우 404 상태코드로 응답한다. 성공한경우에는 json()함수로 응답해준다.



*delete

삭제할 때는 destory()메소드를 사용한다. id기준으로 삭제하는 것이므로 where 이용해 파라미터를 넘겨준다.



*update

update 조금 특별하다. 테스트 코드를 먼저 작성하고 컨트롤러 함수를 만들 이다. 이렇게 코딩하는 방법을 tdd(test-driving develop) 개발방법론이라고 한다.

테스트 코드를 api/user/user.sepc.js 추가하자.



update api 호출하면 200 상태코드가 응답되는지 체크하는 코드이다. 테스트를 돌려보면 당연히 실패가 나올 것이다.

  • 블로그에서는 실패가 출력된다고 했는데 나는 이상하게 passing 출력된다. 이유가 뭘까?


밑에 댓글을 보니 이런 내용이 있다.





다시 블로그의 내용으로 돌아가서 

404에러 메세지가 나오는데 라우팅 설정을 하지 않았기 때문 그럼 테스트를 통과할 있도록 코드를 추가해 볼것이다. 먼저 /api/user/index.js 파일에 해당 api 대한 라우팅 설정을 추가한다.


PUT /users/:id 요청이 들어올 경우 update 컨트롤러 함수가 동작하도록 설정했다.

api/users/user.controller.js파일로 이동하고 update 함수를 정의하자.


update()함수에서는 요청이 들어오면 send()함수를 이용해 200 상태코드만 응답하도록 변경하였다. 그리고 나서 다시 테스트를 돌려보면 테스트에 통과한다.



*중간점검   구조 정리

./

./app.js: 서버 구동 db sync
./models.js: db table row 정의

./api/users/

index.js: rest api  함수 초기화

user.controller.js: 실제 rest  api함수들의 동작을 정의

user.spec.js: test 구동을 위한 코드


  • 실행 CRUD 기능 동작 여부

    -GET /users


-GET /users/:id


-GET /users/:name

(구현 아직 안함)

-POST body={name: string}



-DELETE /users/:id




- PUT /users/:id 




-> update(PUT) 기능 동작

출처:  http://webframeworks.kr/tutorials/expressjs/expressjs_orm_two/




  • npm test 기능 동작 여부
    -GET, PUT


GET, PUT 이렇게 2개의 passing 떠야 하는데 현재 PUT 검사하는중 게다가 PUT 200 status response 중임.


-> 해결


원래 it.only( … ) 되어있던 코드를 

it( … )으로 변경함



-DELETE POST

DELETE POST TABLE length 변경하므로 코드 리펙토링 구현하도록 한다.


지금까지 만든 것은 https://github.com/kiryun/NodeJS_REST-API_tutorial 의 Second Commit에 올려놨다.


출처: http://webframeworks.kr/tutorials/nodejs/api-server-by-nodejs-04/


*Mocha
노드에서 가장 유명한 테스트 툴은 Mocha이다.
Mocha 테스트 코드를 구동시켜주는 테스트 러너이다.Mocha역시 노드로 작성된 패키지중 하나로 npm으로 설치 가능

$ npm i mocha —save-dev

—save-dev 옵션은 개발환경을 위한 패키지 의존성을 설정하기 위한 것이다. 명령을 실행하고 나면 package.json devDependencies속성에 모카 설치정보가 추가된다.


  • 패키지파일의 의존성에 대해
    npm
    프로젝트에 사용되는 노드 패키지 모듈들을 모두 package.json 기록한다.
    대부분은 dependencies 속성에 패키지명과 버전이 적힌다. 서버에 코드를 배포한뒤 서버에서 npm install —production 으로 노드 패키지들을 설치하게 되는데 이때 노드는 package.json dependencies 참고해서 여기에 있는 것들을 설치한다.

    그럼 devDependencies 언제 사용할까?
    ->
    이것은 순전히 개발자를 위한 정보이다. 코드를 서버에 배포하지 않고 다른 개발자가 코드를 코드 저장소에서 다운로드했을
    개발자는 npm install 필요한 노드 패키지를 설치한다. 이때 노드는 똑같이 dependencies속성에 있는 패키지들을 설치한다.
    그리고 —production 없기 때문에 devDependencies속성에 있는 패키지들도 추가로 설치하는 것이다.

    다시 모카로 돌아와서
    테스트 코드는 test suite test 구분할 있다. 수트는 테스트들을 모아놓은 하나의 환경이라고 생각하면 되고, 테스트가 실제 테스트를 수행하는 코드이다.
    모카에서는 각각 describe() it()함수로 기능을 제공한다.
    -describe():
    테스트 수트
    -it():
    테스트
    함수를 이용하여 테스트 코드를 작성해보자. User API 대한 테스트 코드는 api/users/user.spec.js 작성한다.(보통 파일명에 spec이라는 단어가 들어가면 테스트 코드이다.)


모카로 작성한 테스트 코드는 기본적으로 이러한 구조를 가짐.
describe()
함수의 첫번째 파라미터로 테스트 수트의 설명을 서술형 문자열로 넣고 

두번째 파라미터로 함수를 입력함. 비동기 로직의 콜백 형식으로 넣는 것이다.

안에는 it()함수를 이용해 실제 테스트 코드를 작성한다.

설치한 모카 패키지는 node_modules폴더에 있음 npm 패키지 중에 실행 파일이 있는 경우 모두 node_moules/.bin 폴더에 링크된 파일이 위치한다. 여기에 저장된 mocha 명령어를 이용해서 테스트를 실행한다.

$ node_modules/.bin/mocah api/users/user.spec.js


npm start 사용했던것 처럼 npm test 대한 스크립트를 package.json  추가해서 테스트 명령어를 간편하게 한다.


package.json




*Should

이번에 추가할 것은 검증 로직이다. describe(), it() 이용해서 테스트를 작성했지만 진짜 테스트 코드( 결과가 맞는지 확인) 아직 없음. 노드 기본 모듈중 assert 모듈을 이용해 보자.


assert모듈의 equal()함수는 개의 파라미터를 받는데 값이 같은 경우 지나가고 그렇지 않을 경우 에러를 던진다. npm test 이용해 테스트를 실행해보자.


assert.equal() 실행결과 에러를 던지면서 모카에서는 위와 같은 결과를 리포팅 한다. 만약 테스트가 통과한다면 성공 메세지를 보여준다.



assert 노드 기본 모듈이긴 하지만 테스트에서는 이것 말고 다른 모듈을 사용하라고 . 노드 공식 페이지에서 그렇게 이야기하고 있음.

가장 많이 쓰는 모듈중 하나가 should이다. 이것은 서술식의 검증을 코드로 작성할 있게해줌.(코드 쓰는게 마치 서술식 문장같음)


$ npm i should —save-dev



(true) 같은가(should be equal) (true)



*supertest

마지막으로 api  테스트를 가능하게 해주는 노드 패키지가 있는데 바로 supertest이다. supertest 우리가 만든 express 서버를 구동한 http요청을 보내고 응답받는 구조인데 응답을 should 검증하면 되는 것이다.


supertest 설치한다.


$ npm i supertest —save-dev


supertest 사용하려면 서버 역할을 하는 express객체를 가져와야 한다. supertest 실행 함수 파라미터로 사용하기 위함이다. app.js파일에서 우리가 만든 app변수를 외부 노출하여 모듈로 만들어 줘야하는데 module.exporets 키워드로 app 노출해 주면 된다.


//…

module.exports = app;


이제 테스트 파일인 api/users/user.spec.js파일에서 모듈을 가져와 슈퍼테스트와 결합해 사용해 본다.




실행할때 원래 켜져있는 서버가 있으면 안된다. 테스트와 동시에 서버 실행이기 때문이다.


request 상수에 슈퍼테스트 모듈을 할당했음. 그리고  app모듈을 request() 함수의 파라미터로 넣었는데 이것은 우리가 만든 express  서버인 app 슈퍼테스트로 테스트하겠다는 의도이다.


슈퍼테스트는 함수 체이닝을 이용해 get()함수로  api요청을 보낸다. expect()함수로 응답 코드를 설정한 실제 요청을 보내고 응답되면 end()함수에 파라미터로 넣은 콜백함수가 동작하게 되는 구조이다. 콜백함수는 err res  개파라미터를 받는데 요청에 실패하면 err 객체가 활성화되고 그렇지 않으면 res.body 통해 응답 바디에 접근할 있음. 여기서는 api 상태코드 200 리턴하는가만 체크하는 코드이다.

그리고 마지막에 done()함수를 호출 했다. 이것은 it()함수의 두번째 파라미터인 콜백함수의 파라미터인데 슈퍼테스트가 http요청을 하는 비동기 로직이기 때문에 모카측에서 it()함수가 종료되는 시점을 알기위해 사용되는 함수이다.


바디를 점검하는 코드이다.



should.be.an.instanceof() 함수를 이용해 응답 바디의 타입이 배열인지 체크했다. 그리고 함수 체이닝을 이용해 배열의 길이가 3인것을 확인했음.


다음 코드는 res.body 배열임을 확인했으므로 배열 메소드인 map() 이용해 배열의 요소를 점검한다. 배열의 요소 user id name프로퍼티를 가지고 있고 가각 숫자형, 문자형임을 확인했음.


모든 검증을 마치면 모카에게 it()함수가 종료됨을 알리는done()함수를 호출하고 테스트를 종료한다.

이와 같은 형태로 나머지 api(get, post, /users/:id ) 테스트코드를 작성하도록 하자.


test-driven development https://github.com/kiryun/NodeJS_REST-API_tutorial 이곳에 올려놨다.


Node.js REST API 코드 리펙토링
출처: http://webframeworks.kr/tutorials/nodejs/api-server-by-nodejs-03/#


*Router

app.js 파일을 보면 거의 100줄이 되었다. 처음에 비해 코드가 많이 길어졌음. 이번에는 코드 리펙토링 하는 것을 해보려고한다.


express 크게 네부분으로 나눌 있다.

- Application

- Request

- Response

- Router

처음 가지는 모두 설명했고 코드로 구현해 봤음.


  • express Router
    express 객체는 기본적으로 get(), post() 따위의 라우팅 설정 함수가 있음. 하지만 우리가 작성했던 방식으로 코드를 작성하게 되면 코드는 파일 안에서 길어지게 되고 결국 가독성이 떨어지게 것임.
    express Router 클래스를 제공하는데 이를 이용하면 라우팅 코드를 모듈화 있다.그렇다 노드의 모듈을 얘기하는 것임. 결국 라우팅 로직을 모듈화하면 이를  require()함수로 불러서 사용할 있는 장점이 있음.

    const express = require(‘express’);
    const router = express.Router();

    router.get(‘/users’. (req, res) => {
    // …
    });

    //delete, post ….

    module.exports = router;

    express 모듈의 Router 클래스로 객체를 만들어 router상수에 할당한다. 그리고 router객체에서 제공하는 get(), delete(), post()  따위의 함수로 라우팅 로직을 구현한다.
    이것은 우리가 express 객체 app 이용한 것과 매우 똑같다. 마지막으로 module.exports 이용해 노드 모듈로 만들었음.

  • User 라우팅 모듈 만들기
    api/users/index.js 라우팅 모듈을 만들어 볼것이다. app.js 있는 라우팅 코드 부분을 이쪽으로 옮긴다. 그리고 app 상수를 모두 router상수로 변경한다.


  • User 라우팅 모듈 사용하기
    app.js 에는 user 라우팅 코드가 없어졌음. 모두  api/users/index.js  파일로 모듈화 되어 이동되었기 때문이다.
    이제는 모듈을 app.js에서 불러와 사용해야 한다. 여기서 중요한 점은
    express Router  클래스로 만든 User  모듈은 express 미들웨어가 것이다. 그렇기 때문에 express 객체 app use() 함수로 미들웨어를 사용할 있게 되었음.

    app.use(‘/users’, require(‘./api/users’));

    다른 미들웨어를 추가하는 것과 다른 점은 파라미터가 두개라는 것이다. use()에서 파라미터를 개사용하는 경우는 라우팅 모듈을 설정할때임.
    코드의 의미는모든 리퀘스트중 경로가 ‘/users’ 시작되는 요청에 대해서는 두번째 파라미터로 오는 미들웨어가 담당하도록 한다.
    그렇기 때문에 api/users/index.js 코드의 /users 시작되는 파라미터를 수정해줘야함.
    -> router.get(‘/:id’, (req, res) => { /* … */ }); 처럼 수정

  • 라우팅 컨트롤러 만들기
    api/users/user.controller.js 파일 생성


index(), show(), create(), destory() 라는 함수를 만들어 모듈로 만들었다. 이제 외부에서 모듈을 require()  함수로 불러서 사용할 있음.
4개의 함수는 4개의  api 연관된 것이다.

-index() : GET /users

-show() : GET /users/:id

-delete() : DELETE /users/:id

-create() : POST /users


함수와 연결된 api 로직. 그러니깐 get(), delete()  따위의 라우팅 함수 두번째 파라미터를 각각의 함수로 이동한다. 그리고 이컨트롤러 모듈을 api/users/index.js 파일에 불러와 사용한다.

코드는 https://github.com/kiryun/NodeJS_REST-API_tutorial 에 올려놨다.

지금까지 작성한 파일을 정리해보면 다음과 같다.

-app.js : express로 서버 설정 및 구동

-api/users/index.js: User API에 대한 라우팅 설정

-api/users/user.controller.js: User API에 대한 실제 로직



VSCode - AWS 개발환경 세팅

출처:  http://investor-js.blogspot.com/2017/12/visual-studio-code-aws-ec2.html


VSCode extension항목에서 ftp-simple 검색 install 클릭



f1 눌러주고 ftp-simple : Config - FTP connection setting 찾아서 클릭



json파일이 보이는데 json파일을 수정해 줘야함


다음과 같이 수정


name: name

host: AWS public IP주소 또는 DNS

port: ssh 이므로 sftp. 따라서 22 포트가 default( ftp 21 )

type: ssh 이므로 sftp

username: aws 접속시 username

password: 없으면 공란

path: 접속 성공시 default경로

privateKeyPath: aws pem 로컬 경로


접속 확인


F1 눌러서 ftp-simple : Remote directory open to workspace 선택




-> Enter the FTP connect password 누르면 아무것도 안뜨는데?? 머지

-> 출처: http://wiki.webnori.com/pages/viewpage.action?pageId=17727521

“privateKeyPath” 아니라 “privateKey”


f1눌러서 enter누르다보면


이런거 뜨는데 그냥 enter누르자.



나옴





Node.js REST API 만들기

출처: http://webframeworks.kr/tutorials/nodejs/api-server-by-nodejs-03/#


*사용자 목록조회 API

  • 임시 사용자 데이터
    우린 아직 데이터베이스는 다루지 않음. 대신에 데이터 베이스 역할을 할수 있는 users 변수를 만들어서 사용할 .
    app.js
    파일에 아래와 같은 코드(배열) 추가한다.



  • 라우팅 설정
    클라이언트는 users 변수에 저장된 사용자 데이터를 조회하도록 API 요청할수 있다. API “GET/users”라고 이름을 짓겠다.
    express
    라우팅 함수 get()함수를 이용하여 메소드가 GET임을 설정한뒤 첫번째 파라미터로 경로명인 “/users” 문자열을 넘겨주면 API 대한 라우팅을 만들 있음.


여기까지 작성하면 클라이언트가 “GET/users” 요청할 경우 위에서 설정한 라우팅 함수가 동작할 것이다. 이제 남은것은 라우팅 함수에서 users변수의 값을 클라이언트에 보내주기만 하면 된다. 

라우팅 함수의 파라미터 두번째 파라미터인 res 응답 객채이다. 응답객체의 함수중 json()함수를 사용하면 json형식의 데이터를 클라이언트에게 보내줄 있음. 

  • JSON
    JSON
    일종의 데이터 표현 방법임. 프로그래밍 언어로 오해X
  • 라우팅 로직 작성
    users
    변수에 들어있는 값은 자바스크립트 객체의 배열이다. res.json() 함수는 파라미터로 이값을 받아 JSON형식으로 변환한다. 그리고 요청한 클라이언트로 JSON데이터를 응답해 주는 기능을 한다.


위에 하이라이팅한 부분은 다음과 같이 변경 가능하다.


app.get(‘//users’, (req, res) => res.json(users));

애로우 함수(=>) 함수 본체에 return 구문만 있을 경우 이렇게 한줄로 작성이 가능하다.

  • API 테스트
    curl 명령어를 이용해 테스트 해본다.

    $ curl -X GET ’127.0.0.1:3000/users’ -v



*특정 사용자 조회 API


현제 users id name으로 구성되어 있음.
name
필드는 중복될수 있지만 id 중복되지 않는 실별자이다. (우리가 만들때 그렇게 생각해서 만들었기때문에) 실제 데이터베이스 테이블에 저장될때도 id 유일한 속성인 unique 설정 것이다. 아직 데이터베이스를 다루지 않기 때문에 우리는 id 대해 유일하다고 가정하고 진행할 .

그럼 클라이언트는 특정 사용자를 조회하는 요청을 서버에게 전달할 id 함께 전달한다. id 1 사용자 객체를 조회할 경우 서버는 users 저장된 배열을 뒤져 id 1 객체를 클라이언트로 응답해 주면 되는 것이다. 그럼 이번 API 대한 라우팅 로직을 구현해 볼것이다.

  • 파라미터 설정하는 방법
    클라이언트에서는 id 1 사용자를 조회할 경우 아래 주소로 서버에 요청할 있음.

    GET /users/1

    만약 id 2라면

    GET /users/2

    서버코드를 작성 해보면 아래와 같이 작성할 있다.

    app.get(‘/users/1’, (req, res) => /* … */); // id: 1 대한 요청 처리

    app.get(‘/users/2’, (req, res) => /* … */); // id: 2 대한 요청 처리

    그러나 이는 비효울적인 방법이다.
    다음과 같이 코드를 설정할 .

    app.get(‘/users/:id’, (req, res) => {
    console.log(req.params.id); // 사용자가 입력한 :id 값이 출력됨. (주의 : 서버로 오는 데이터는 전부 문자열 형식이다. 기억할 .)
    });

    $ npm start
    $ curl -X GET ’127.0.0.1/users/1’

    결과를 보면 다음과 같다.


users/1 GET하면
terminal 1 출력이 되고
users/2 GET하면 

terminal 2 출력이 된다.


  • ID 유저 객체 검색
    우선 클라이언트로부터 요청정보를 받는 것에 성공. 이제는 id 기반으로 서버에 있는 users 배열을 뒤져 id 일치하는 데이터를 찾아 요청한 클라이언트로 응답해준다.

    id users 배열을 검색하기 전에 id값에 대한 처리가 남아 있음. 값이 숫자인지를 확인 하는 .
    숫자가 아닌 데이터는 유효하지 않은 데이터이기 때문이다. 예를 들어 “GET /users/alice” 라고 요청할 경우 우리가 설정한 url규칙에 맞지 않음.
    이런경우 다음과 같이 처리하도록 한다.

    app.get(‘/users/:id’, (req, res) => {
    const id = parseInt(req.params.id, 10);
    });

    parseInt() 함수로 id 문자열 값을 정수형으로 변경했다.( users 있는 값은 숫자형 데이터인데 클라이언트가 요청한 데이터는 문자열로 들어오기 때문)
    parseInt() 문자열을 숫자로 변경하는 과정에서 에러가 발생하면 NaN 되돌려주게 되어있음. 이것은 명백히 요청한 클라이언트쪽의 실수라고 있음.
    서버는 이러한 요청에 대해서도 클라이언트에게 적절한 응답을 해줘야함.

    app.get(‘users/:id’, (req, res) => {
    const id = parseInt(req.params.id, 10);
    if(!id){
    return res.status(400).json({err: ‘Incorrect id’});
    }
    });
  • 함수체이닝



  • 클라이언트로 응답
    클라이언트가 요청한 id정보를 검증하였다. 이제 남은 것은 users배열에서 id 일치하는 user 객체를 찾는 일이다.

    코드 작성전에 filter()함수의 사용법에 대해 간단히 짚어넘어 가도록한다.



*
우리의 코드에 원본 작성자가 제시한 filter() 쓰게 되면 다음과 에러가 출력되어서 쓰지 않는다.



  • 404 에러
    만약 filter() 함수로 검색에 실패할 경우 배열( [] ) 반환하게 된다.그리고 배열의 0 인덱스에 접근하게되면 undefined값이 리턴된다.
    결국 user변수에는 undefined값이 저장되는 것이다. 이러한 경우 REST API 규칙에 의해 404 상태코드를 클라이언트에게 알려 줘야 한다. 왜냐하면 id 해당하는 유저 데이터가 없기 때문
    다음의 코드를 추가한다.

    app.get(‘/users/:id’, (req, res) => {
    let user = users.filter(user => user.id === id)[0];
    if(!user){
    return res.status(404).json({err: ‘Unknown user’});
    });
  • 성공 응답


  • 성공 응답 404, 400 출력 결과



*특정 사용자 삭제 API

API 주소는 아래와 같다.

DELETE /users/:id

  • 라우팅 설정
    메소드만 GET에서 DLETE 바뀌었고 뒤에 경로는 조회  API 동일하다

    라우팅을 먼저 설정한다. 그동안 express객체 app get() 함수만 사용했는데 이것은 조회 API 메소드가 GET이었기 때문이다. 삭제 API 메소드인 DELETE 설정하려면 delete() 함수를 사용해야 한다.
    delete()  함수의 파라미터도 get() 함수와 같음. 첫번째 파라미터로 설정할 경로를 문자열로 넘겨줌.

    app.delete(‘/users/:id’, (req, res) => /* … */ );

    파라미터로 받은 id 값에 대한 처리도 해야하는데 이미 “GET /users/:id” API 만들면서 사용했음. 동일한 코드를 사용하면 된다.

    app.delete(‘/users/:id’, (req, res) => {
    const id = parseInt(req.params.id, 10);
    if(!id){
    return res.status(400).json({err: ‘Incorrect id’});
    }
    });
  • 삭제 로직 구현
    잠깐 삭제 API 로직을 생각해보면, 먼저 유저 데이터를 담고 있는 users배열에서 id 해당하는 객체의 위치를 찾아야한다. 그리고 찾은 위치의 요소를 배열에서 제거하면 것이다.
    자바스크립트 배열 메소드중에는 이러한 기능을 잇는 함수가 있는데 다음 두가지 함수를 사용할 것이다.

    findeIndex()
    splice()

  • 배열에서 삭제할 유저 찾기
    findIndex() 배열을 순회하면서 어떤 기준에 맞는 요소의 인덱스를 찾는데 사용한다. users에서 요청한 id 해당하는 객체가 있는 인덱스를 먼저 찾아야함.

    const userIdx = users.findIndex( user => {
    return user.id === id;
    });

    앞에서 보았던 배열 메소드 filter() 유사하게 동작한다. findIndex() 배열의 요소를 순차적으로 돌면서 계산한다. user.findIndex( user, => ) 구문의 경우 처음  user users배열의 요소가 반환되어 나온다. user 대해 id값을 비교한 참을 반환하면 users배열에서의 user 있는 배열 인덱스가 반환된다. 결국 인덱스 정수값은 userIdx 상수에 저장되는 것이다. 만약 id  비교 결과 거짓이 리턴되면 배열의 다음 요소가 user  변수가 할당되어 넘어온다.

    if(userIdx === -1){
    return res.status(404).json({err: ‘Unknown user’});
    }

  • 배열에서 유저 객체 제거
    삭제할 유저 객체의 인덱스를 찾았으면 배열에서 인덱스를 이용해 요소를 삭제해야 한다.
    splice()
    함수를 사용한다.
    첫번째 파라미터로 삭제할 인덱스 숫자를 넘겨주고 두번째 파라미터는 1 넣어준다. 1이라는 값은 첫번째 파라미터를 포함하여 다음 요소 몇개를 삭제하겠냐는 의미이다.
    우리는 1개만 삭제하기 때문에 1 넘겨준다.

    users.splice(userIdx, 1);
  • 응답
    서버쪽에서 데이터를 다루었으니 이제 요청한 클라이언트에게 뭔가 응답해줘야 한다. 사용자를 조회할때는 조회한 사용자 객체를 응답했는데 삭제의 경우는 삭제된 데이터를 보내줄수도 없는 일이다. 보통 두가지 방법이 있다.
    첫번째는
    삭제된 전체 users배열을 다시 응답하는 방법이다. 클라이언트 입장에서는 응답으로 users 배열을 확인하면 요청한 데이터가 삭제되었는지 확인할 있기 때문이다.
    두번째는
    아무 데이터도 보내지 않는 것이다. 여기서 우리는 REST API 상태코드를 사용할 있다. 바로 “No content” 뜻하는 204 상태코드를 응답하는 .

    여기서는 후자의 방법을 사용한다.



처음 get 해을 id: 1 , name: hyun 제대로 출력이 되었고
다음 delete후에 다시 get 404 error 리턴해주는 것을 있다.


*사용자 생성 API

POST /users

  • 라우팅 설정
    express 객체의 함수중 get() delete() 사용해봤다. 이번에는 post()함수를 사용해야한다.

    app.pos(‘/users’, (req, res) => /* … */);

  • 요청 body
    http 요청에 사용되는 데이터는 두가지 방법이 있음

    쿼리 문자열
    body

    쿼리 문자열은 url 포함되어 있는 key/value  값을 의미한다.
    예를 들면 구글에 chirs라는 검색을 했을 주소창에 아래와 비슷한 형태의 주소가 있을 것이다.

    https://www.google.co.kr/#newwindow=1&q=chirs

    https 부분을 프로토콜이라고 한다.
    www.google.co.kr 도메인이라고 한다.
    /#newwindow=1 부분을 경로라고 한다.
    &q=chris 쿼리 문자열 이라고 한다.

    브라우저가 서버로 뭔가 요청할때 q=chirs  라는 형식으로 요청하는 것이다.
    한편 body라는 형식으로 요청데이터를 보낼 있는데 웹브라우저로는 확인하기 어려움. body 데이터는 post 메소드일 경우에만 유효하기 때문
  • body-parser
    express 요청 body 데이터에 접근하기 위해서 doby-parser라는 패키지를 추가해야한다.

    $ npm i body-parser —save

    body-parser 미들웨어이다. 따라서 app.js 있는 express객체에 미들웨어를 추가해서 사용할 있다.
    미들웨어를 추가할 사용하는 express객체의 함수는 use() 함수이다.

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended: true}));
  • 파라미터 검증
    비로소 우리 코드에서 요청 body 접근할 있게 되었다. 어떻게 접근하는가?
    -> id 파라미터에 접근하는 방법과 유사하다. 바로 req.body 통해서 body값에 접근할 있다. 클라이언트가 요청시 body name=“hyun”라는 형식으로 요청한다고 생각해보자. 그럼 서버에서는 req.body.name=“hyun”형식으로 데이터가 전송된다.

     app.post(‘/users’, (req, res) => {
    const name = req.body.name || ‘’;
    });

    name 상수에 req.body.name 할당하는 코드를 작성했다. 하지만 만약에 값이 undefined 있는 경우가 있다. 클라이언트가 요청시 name값을 입력하지 않은 경우이다.
    그런경우에는 문자열을 name 상수에 넣도록 하겠다.
    그러나 이는 서비스 관점에서 올바르지 못한방법이다.
    -> 400 Bad Request 응답을 보낼 것이다.

    if(!name.length){
    return res.status(400).json({err: ‘Incorect name’});
    });
  • 새로운 아이디 만들기
    요청한 파라미터 name 제대로 입력되었다면 다음 할일은 새로운 유저 객체를 만드는 . 유저객체는 id name으로 구성되어 있는데, 방금 name 요청 바디를 통해 얻었다.
    남은 것은 id이다. 이것을 얻으려면 기존에 있는 id 중복되지 않은 값을 사용해야 한다. 간단하게 기존에 있는 id 값에서 +1 증가시키면서 추가하기로 한다.
    javascript  배열에서 제공하는 메소드를 사용한다. reduce() 함수

    const id = users.reduce((maxId, user) => {
    return user.id > maxId ? user.id : maxId
    }, 0);

    reduce()  함수는 첫번째 함수를 파라미터로 넘겨주는데 함수는 개의 파라미터를 갖는다.
    첫번째 파라미터로 maxId 우리가 reduce() 함수가 종료될 얻게될 값을 저장하고 있음.
    두번째 파라미터는 배열 users 요소를 순서대로 반환해주는 user객체 이다. 그리고 reduce()함수의 두번째 파라미터로 0 넘겨줬는데 이는 maxId 초기 값이다.

    예를 들어 users.reduce(maxId, user) => {}, 0); 구문이 처음 실행될 maxId 값은 0 되고  user users 배열의 첫번째 요소가 된다.

    코드의 두번째 줄을 살펴보면 어떤 값을 반환하고 있는데 값은 다음 반복문에서 maxId 값이 되는 것이다.
    위의 return하고 있는 삼항연산자는 아래와 같은 코드이다.

    if (user.id > maxId){
    return user.id;
    }else{
    return maxId
    }

    결국 id상수에는 users배열에 있는 id 가장 값이 들어 것이다. 우리는 새로운 id 만들 것이기 때문에 값보다 1 값으로 id상수를 만들 것이다.

    const id = users.reduce((maxId, user) => {
    return user.id > maxId ? user.id : maxId
    }, 0) + 1;
  • 배열에 유저 추가하기
    받은 값을 newUser 상수에 할당한다.

    const newUser = {
    id: id,
    name: name
    };

    그리고 기존의 users배열에 새로운 유저를 추가해준다.

    users.push(newUser);
  • 응답
    마지막으로 서버는 요청한 클라이언트에게 응답을 보내야한다.
    새로만든 유저 데이터를 보내는 것이 합당해 보인다. 그리고 또한가지로 상태코드를 보내는데 “201 Created” 코드를 보내는 것이 REST API형식을 따르는 것이다.

    return res.status(201).json(newUser);

bodyParser 선언


post구현


post get 결과



*Error: listen EADDRINUSE :::3000 에러 해결방법
출처: https://tom7930.tistory.com/27


이런식의 에러가 출력된다면 포트를 이미 사용중이라는 말이다.

아마 노드서버가 정상적으로 종료되지 않았을 것이다.

따라서 노드 서버를 죽일 것이다.

일단 프로세스의 pid를 찾아보자


$ ps -ef | grep app.js



node 서버의 pid 13869이다. 죽이면 된다.


$ kill -9 13869





AWS에서 nodeJS 설치


출처: http://webframeworks.kr/tutorials/nodejs/api-server-by-nodejs-01/#tocAnchor-1-10


  • NodeJS 대해
    자바스크립트 엔젠에는 크롬에서 사용하는 V8, 사파리에서 사용하는 Webkit, 파이어폭스에서 사용하는 SpikerMonky등이 있음.
    이러한 엔진 중에 구글에서 만든 V8 엔진이 노드에서 사용하는 자바스크립트 엔진이다.
    Ryan Dahl
    이라는 사람은 V8 엔진에 이벤트 I/O 프레임웤과 CommonJS명세를 이용한 모듈을 결합하여 Node.js 만들었음.
  • 설치
    $ sudo apt-get update

    $ sudo apt-get install nodejs

    $ sudo apt-get install npm

  • NPM
    npm
    노드로 만든 패키지를 관리하는 툴이고 “Node Package Manager” 약자이다.
    우리는 npm 몇가지 기능을 이용해서 프로젝틀글 진행 할것이다. 우선 우리가 만들 프로젝트 폴더를 만들고 거기에 npm으로 프로젝트를 초기화 한다.

    $ npm init

몇가지 물어보는 일단은 엔터를 입력하면서 기본값을 사용한다고 답변하면 된다.

모두 완료하면 위의 그림 처럼 package.json 생성된다.


  • nodejs.org 사이트로 이동한다.
    웹서버를 만들수 있는 간단한 코드를 제공하는데 이것을 복사하여 우리 프로젝트 폴더의 app.js 파일로 만든다.

    app.js


$  node app.js

npm 이요해서 프로젝트를 관리하려면 package.json 파일에 스크립트를 등록해 주는 것이 좋음

start라는 스크립트를 만들어 보자.(npm init 하면서 만들어진 package.json 수정한다.)


~/Documents/tutorial/package.json



$ npm start



  • Curl 리퀘스트 보내기
    브라우저는 내부적으로 GET http://127.0.0.1:3000 요청을 보내서 결과를 화면에 뿌려주는 역할을 .
    http
    요쳥은 외에도 헤더에 다양한 정보를 담아서 보낼수 있는데 브라우저는 모든 정보를 설정하기에는 기능이 제한적이다.

    구체적인 요청을 위해 앞으로는 curl 이란 프로그램을 사용할 .
    아래 명령어로 다시 서버에 요청을 보내본다.

    터미널에서 aws EC2 접속한 다음
    $ npm start

vscode terminal ( VSCode 아니어도 그냥 aws 접속되어 있는 어느 곳이든)에서 


$ curl -X GET ’127.0.0.1:3000’


다음과 같이 Hello World! 출력되는 것을 있다.


**백그라운드에서 Server 돌리고 싶다면 HashtagPlayer_개발 에서 잡업로그 섹션에 백그라운드로 서버를 실행시키는 방법이 적혀 있음


다음은 -v  옵션을 추가하여 명령어를 실행 해본다.

$ curl -X GET ’127.0.0.1:3000’


다음과 같이 출력이 되는 것을 있다.




  • 익스프레스를 사용하는 이유
    익스프레스는 노드를 만든 패키지의 일종이다. 서버를 만들기위한 것이라고 있음.

    자세한 사용법은 http://expressjs.com/ 나와 있음.

    그런데 질문. API서버를 만드는데 익스프레스를 사용하려는 것일까?
    ->
    우리가 만들 API 서버에 대해 다시 생각해보자. 서버는 클라이언트의 어떠한 요청이 있을 경우 서버에서 자원을 처리한뒤 결과를 다시 클라이언트로 보내줌. 클라이언트는 서버에게 요청할 API라는 것을 통해서 요청할수 있는데 어떤 일정한 규칙이 있어야 .
    한국사람이 중국 타오바오에 전화해서 한국말로 지껄일수는 없기 때문

    클라이언트와 서버는  HTTP라는 규칙을 이용해서 서로 통신하게 . 웹에서도 HTTP 이용해 페이지를 주고 받음. 익스프레스가 프레임워크이긴 하지만 HTTP기반의 API서비스를 개발하는 것이기 때문에 API서버에서도 사용하는

    또한 프레임워크를 사용하게 되면 노드로만 코드를 작성하는 보다는 훨씬 빠른시간에 효율적으로 서버를 개발할수 있는 이점도 있음.
  • express 설치
    익스프레스도 일종의 노드 패키지이기 때문에 npm으로 설치할 있음.

    $ npm install express —save
  • expcress Hello world 변경하기
    기존의 app.js express 패키지를 이용해서 만들어 본다.





출처: http://www.newthinktank.com/2018/03/setup-visual-studio-code-macos/

1. Xcode 설치( g++ )


2. VS Code의 extensions에서 C/C++ 설치/Reroad


3. VS Code의 extensions에서 Code Runner 설치/Reroad


4. FlatformIO IDE 설치/Reroad


5. project 생성



6. 업로드/시리얼 모니터 출력


개인적으로 프로그래밍 기초를 공부한다고 했을 때 그 순서와 읽을 책을 추천 해본다.


공부순서를 이야기하기 전에 공부방식에 대해 짤막하게 설명한다.


이 말은 나의 멘토님께 들은 이야기다.

공부를 하면서 얻을 수 있는 지식의 양이 100이라고 했을 때

프로그래밍 공부에서는


10은 책읽기 및 강의 듣기

20은 예제 코드 따라 써보기

70은 직접 큰 프로젝트든 작은 프로그램이든 100% 내 머리속에서 나온것을 만들어 보기


라고 한다.

백날 책읽고 강의만 듣는다고 해서 나아지는 건 별로 없다는 것이다. 직접 해봐야 한다.


다음으로 이야기할 기초공부를 모두 끝마치고 나면 새로운 기술을 배우는데에 준비가 되었다고 생각한다.


1. 첫 프로그래밍 언어는 C언어를 추천한다.

문법 책은 [윤성우] 님이 쓰신 [열혈강의 C] 또는 [서현우]님의 [뇌를 자극하는 C프로그래밍] 이다.

C가 어렵다고 하는 대부분의 이유중 하나가 포인터 때문인데 포인터를 공부하는 부분부터는 이 책을 추천한다.

[리처드 리스]의 [C포인터의 이해와 활용]


문법을 다 공부 했다면 이 책은 선택이다. 앞으로 많은 것을 공부하게 될텐데 맛보기로 뭐가 있는지를 전반적으로 한번 훑어보기에 좋은 책이다.

[양태환]님의 [컴퓨터 사이언스 부트캠프 with 파이썬]


2. 언어를 배웠으니 이를 써먹어 봐야한다. 코드는 알고리즘의 집합체이며, 알고리즘은 생각의 순서이다.

code.plus 강의 중에 알고리즘 기초라는 동영상 강의가 있다.

강의를 듣다가 문제풀이 부분이 나오면 잠깐 멈추고 https://www.acmicpc.net/ 으로 들어가 먼저 한번 [풀어보거나] [최대 4시간정도 풀어보고 안풀리는] 경우 

다시 강의를 이어 듣는 것을 추천한다. 


3. 자료구조는 선택이 아닌 필수다.

책은 [윤성우]님이 쓰신 [열혈강의 자료구조]이다.


4. OOP 언어를 배울 차례가 된 것 같다. 언어를 배웠으니 말을 효율적으로 하는 방법을 배워야 한다.

그리고 OOP언어의 문법만 배울 것이 아니라 디자인패턴도 같이 배우는 것을 추천한다.

문법 책은 [윤성우]님이 쓰신 [열혈강의 C++]또는 [남궁성]님의 [자바의 정석]이고

디자인 패턴 책은 [에릭 프리먼 , 엘리자베스 프리먼 , 케이시 시에라 , 버트 베이츠]의 [Head First Design Patterns: 스토리가 있는 패턴 학습법]를 추천한다.


5. 이제 언어도 배웠고 말하는 법도 배웠고 말을 잘하는 방법도 배웠다. 그나라의 문화를 제대로 알려면 이제 역사를 배울 차례다.

이론서적은 컴퓨터 구조, OS, 컴퓨터 네트워크 3가지 파트를 추천한다.


컴퓨터 구조:

[David A. Patterson, John L. Hennessy]의 [컴퓨터 구조 및 설계]


운영체제:

[아브라함 실버스카츠]의 [Operating System Concepts] 또는 


시간이 많다면 OS를 직접 만들어 보는 것도 추천한다. 컴퓨터 구조와 OS이론의 실기책이라고 할 수 있다.

[한승훈]님의 [IT EXPERT, 64비트 멀티코어 OS 원리와 구조 1권: OS 개발 60일 프로젝트] 1권 2권으로 나눠져있다.


컴퓨터 네트워크:

[진강훈]님의 [후니의 쉽게 쓴 시스코 네트워킹], [미즈구치 카츠야]의 [모두의 네트워크: 10일 만에 배우는 네트워크 기초]


6. 알고리즘은 개발자라면 시간 날때마다 틈틈이 해야할 기본소양이므로 항상 공부하도록 한다.


화이팅.


책을 고를 때는 이분의 블로그를 많이 참고를 했다.

https://www.sangkon.com/2016/02/10/good_books_for_dev/


----------------------------------------------------------------------------------------------------------------------------

독학의 단점은 혼자만의 생각에 갇혀있을 위험이 매우 크다는 것이다.

본 블로그의 목적은 나의 생각을 모두에게 공개하고 그에 대한 다른 사람들의 생각을 듣고 공부의 질을 높이는 것에 있다.

따라서 어떠한 의견이든 환영이다.

----------------------------------------------------------------------------------------------------------------------------





+ Recent posts