본문 바로가기

4. Node.js | React.js

5/2(화) IT K-DT(43일차) / twitter 예제(1)

 

1. twitter 예제 refactoring 사전작업

 

1. Client 기본파일을 다운로드

client - 복사본.zip
0.07MB

(Twitter의 기능이 있는 client 기본파일)

2. Client가 있는 디렉토리에 Server 폴더 생성

index.js 파일 생성/작성 후 폴더 내부에 controller, data, router 폴더 추가 생성.

가능하다면 nodemon 툴, gitignore 파일도 생성.

 

3. server와 client 폴더 내 node.js 설치 

    1. cd C:\yjcho\Node.js\project\server 로 경로 변경.

    2. npm audit fix --force    // 보안 취약점을 수정해주는 코드

    3. npm audit fix -force

    4. npm i (node_modules의 복원).

    5. npm start

 

아래와 같이 트위터형식의 기본적인 프로그램이 localhost에서 실행됨.

 

사용 경로: C:\yjcho\Node.js\Project_test\Server\index.js

전체 화면을 렌더링해주는 기능. 아래 내용 작성.

 

server단에서 이용되는 모듈들의 설치도 잊지말고 해주어야 함.


import express from "express";
import cors from "cors";
import morgan from "morgan";
import tweetsRouter from "./router/tweets.js";

const app = express();
app.use(express.json());
app.use(cors());
app.use(morgan("tiny")); // 사용자들이 들어오게되면 로그를 콘솔에 찍어줌

app.use("/tweets", tweetsRouter);

app.use((req, res, next) => {
    res.sendStatus(404);
});

app.use((error, req, res, next) => {
    console.log(error);
    res.sendStatus(500);
});

app.listen(8080);

 

 

 

react.js도 이용함.

→ .jsx 파일 內 react.js를 사용한 예시

 

 

첨부한 client와 생성한 server파일을 위주로 앞으로 twitter 예제의 refactoring을 진행할 예정

 

아래 경로에 디렉토리 및 파일 생성

C:\yjcho\Node.js\Project\Server\controller\tweets.js

C:\yjcho\Node.js\Project_test\Server\data\tweets.js

 

2. Routing 기능 실습

교육 간 사용할 경로: C:\yjcho\Node.js\project\Server\router\tweets.js

 

아래의 5가지 기능을 구현해 볼 예정.

 

1) 'GET' 을 이용username을 통한 data 출력

2) 'GET' 을 이용id를 통한 data 출력

3) 'POST'를 이용id: Date.now().toString()을 id에 추가 후 출력

4) 'PUT'을 이용 → id를 통한 text 수정 

5) 'DELETE'를 이용 → data를 삭제

 

 

아래의 'tweets 배열'과 'node.js express 모듈' 및 'postman 프로그램'을 이용하여 routing을 진행.


import express from 'express';

let tweets = [
    {
        id:'1',
        text:'첫 트윗입니다',
        createdAt: Date.now().toString(),      // 현재 시간을 문자열로 변경
        name: 'apple',
        username: '김사과',
        url: ''
    },

    {
        id:'2',
        text:'안녕하세요',
        createdAt: Date.now().toString(),      // 현재 시간을 문자열로 변경
        name: 'banana',
        username: '반하나',
        url: ''
    }
];

const router = express.Router();


... 
... 
... 
... 

export default router;                                 // router를 내보내기함.

 

아래의 번호들을 

... 
... 
... 
... 

에 삽입함.

 

1) 'GET' 을 이용  username을 통한 data 출력


router.get('/', (req, res, next)=> {                         // route경로에 대한 get 요청이 들어올 시 실행할 익명함수.
    const username = req.query.username;        // 요청의 쿼리 파라미터에서 username 값을 가져와 변수에 저장.
    const data = username
        ? tweets.filter((tweet) => tweet.username === username)
                    // username 값이 존재하면(truthy 값) tweets 배열에서 해당 유저의 tweet만 필터링한 결과를 data 변수에 할당.

        : tweets;              // username 값이 존재하지 않으면 (falsy 값) 모든 tweets을 data 변수에 할당.
    res.status(200).json(data);         // HTTP 응답 코드를 200으로 설정하고, data를 JSON 형태로 클라이언트에게 반환.
});

 

해당 코드 작성 후 체크목적 Postman 프로그램 실행.

 


REST API의 모음집을 생성하는 작업.
My Workspace 內 왼쪽의 'Collections' 탭에서 'new'를 클릭 → 'Collection'을 한번 더 클릭.

 

 

'Twitter'라는 Collection을 새로 생성.

새로 생성한 Collections 탭에서 아래와 같이 Variables 탭의 내용을 작성 후 Save.

 

 

1) 에서 삼항연산자의 false return값인 tweets; 의 파일을 생성하는 과정

우측 점 3개 클릭 후 Add request 클릭.

 

 

Twitter 內 경로의 새로 생성한 Request의 name을 'Get Tweets'로 수정.

GET의 주소창을 {{base}}/tweets 로 작성.

(여기서 {{base}}는 이전에 지정해두었던 http://localhost:8080 주소임.)

 

 

1) 의 삼항연산자에서 tweets.filter((tweet) => tweet.username === username) filter의 data를 생성

같은 파일을 Copy 후 이름을 'Get Tweets by username'으로 변경.

 

 

GET의 주소창에서 ' {{base}}/tweets?username=김사과 ' 를 작성.

1)의 코드에서  const username = req.query.username; 의 부분 中 query에 해당하는 부분.

(URL의 query 매개변수: URL 끝에 ?와 함께 전달되는 매개변수)

Key: username

Value: 김사과

 

작성 후 Save & Send.

 

 

정상적으로 실행될 때의 화면.

(오류가 발생하는 경우, 서버를 Ctrl + C 로 닫았다가 다시 오픈하면 정상적으로 작동하는 경우도 있음.)

 

 

2) 'GET' 을 이용  id를 통한 data 출력


router.get('/:id', (req, res, next)=> {                           // id를 통해 router의 get 메서드를 이용함

    const id = req.params.id;
    const tweet = tweets.find((tweet) => tweet.id === id);

    if (tweet) {
        res.status(200).json(tweet);
    } else {
        res.status(404).json({message: `Tweet id(${id}) not found`});
    }
});

 

id가 1인 경우를 찾으려 함.

주소창에서 {{base}}/tweets/1 입력

Save + Send 시 'Get Tweets'에서 정상적으로 작동하는 것을 확인할 수 있음.

 

3) 'POST'를 이용  id: Date.now().toString()을 id에 추가 후 출력


router.post('/', (req, res, next)=> {

    const {text, name, username} = req.body;
    const tweet = {
        id: Date.now().toString(),
        text: text,
        createdAt: new Date(),
        name: name,
        username: username
    };

    tweets = [tweet, ...tweets];           // tweets에 tweet이 추가가 되어 tweets가 되었다는 의미.
    res.status(201).json(tweet);
});

 

오늘의 날짜가 문자열로 받아진 후 id의 뒤에 출력됨.

POST 주소창에서 {{base}}/tweets 입력 후 

Body>raw 에서 아래와 같은 내용을 입력하여 json을 추가.

 

Save + Send 시 정상적으로 입력한 json이 추가된 것을 확인할 수 있음.

 

 

 

4) 'PUT'을 이용 → id를 통한 text 수정 


router.put('/:id', (req, res, next)=> {                    // id를 받아서 text를 찾기
    const id = req.params.id;
    const text = req.body.text;
    const tweet = tweets.find((tweet) => tweet.id === id);

    if (tweet) {
        tweet.text = text;
        res.status(200).json(tweet);
    } else {
        res.status(404).json({message: `Tweet id(${id}) not found`});
    }
});

 

id가 1인 배열의 text를 변경

아래와 같이 PUT 주소창에 {{base}}/tweets/1 입력 후 

Body>raw 에서 아래와 같은 내용을 입력하여 변경될 json을 추가.

 

Save + Send 시 정상적으로 text가 변경된 것을 확인할 수 있음.

 

 

5) 'DELETE'를 이용 → data를 삭제


  router.delete('/:id', (req, res, next) => {
    const id = req.params.id;
    tweets = tweets.filter((tweet) => tweet.id !== id); // 삭제할 id를 제외하고 나머지를 필터로 출력하는 구조.
    res.sendStatus(204);
});

 

아래와 같이 DELETE 주소창에 {{base}}/tweets/1 입력 후

Save + Send 시  'id'=1을 찾을 수 없다고 출력되며 정상적으로 삭제가 된 것을 확인할 수 있음.

 

 

3. fetch 기능 실습

교육 간 사용할 경로: C:\yjcho\Node.js\project\Client\src\service\tweet.js

 

tweet에 이용할 CRUD기능을 모두 가져옴



 export default class TweetService {             // TweetService라는 클래스를 새로 생성하여 기본적(default)으로 내보내게 함.
  ​

 tweets = [                  // tweets의 list data

    {
      id: 1,
      text: '첫번째 트윗이예요!',
      createdAt: '2022-05-09T04:20:57.000Z',
      name: 'apple',
      username: '김사과',
      url: 'https://widgetwhats.com/app/uploads/2019/11/free-profile-photo-whatsapp-1.png',
    },
  ];

// async : 함수를 비동기함수로 선언하는 역할을 함. 주로 함수 내부에서 await를 같이 사용하여 비동기작업의 완료까지
   함수의 실행을 중지시킬 수 있음.


 async getTweets(username) {    //  함수 getTweets:  fetch를 통해  tweets ? username= : username의 삼항연산식을 가져옴.
      return username
        ? this.tweets.filter((tweet) => tweet.username === username)
        : this.tweets;
    }
  ​

    async postTweet(text) {                //함수 postTweet:  fetch를 통해 /tweets post로 입력한 데이터를 전송.
      const tweet = {
        id: Date.now(),         // 현재 시간을 밀리초단위로 가져와 id의 속성값으로 사용.
        createdAt: new Date(),   // 현재 시간을 가져와 createdAt의 속성값으로 사용.
        name: 'apple',
        username: '김사과',
        text: 'text',    // text 인자로 받은 값을 그대로 text의 속성값으로 사용.
      };
      this.tweets.push(tweet);   // this.tweets 배열에 tweet 객체를 추가하여 return으로 tweet 반환.
      return tweet;
    }
  ​

    async deleteTweet(tweetId) {          //함수 deleteTweet:  filter를 통해 tweet의 아이디와 tweetId와 값이 다르면, 데이터를 삭제.
      this.tweets = this.tweets.filter((tweet) => tweet.id !== tweetId);
    }
  ​

    async updateTweet(tweetId, text) {  //함수 updateTweet:  find를 통해 tweet의 아이디와 tweetId와 값이 같으면 반환.
      const tweet = this.tweets.find((tweet) => tweet.id === tweetId);     
      if (!tweet) {                                      // 만약 tweet가 존재하지 않으면, throw를 통해 에러와 에러메시지를 출력.
        throw new Error('tweet not found!');
      }
      tweet.text = text; // 만약 tweet가 존재하면, tweet객체의 text를 우항의 text로 업데이트하여 return으로 반환.
      return tweet;
    }

}

 

4. validation 기능 실습

validation과 관련된 모듈의 설치가 필요함.

 

npm i express-validator

npm i express

을 입력하여 설치.

 

예) text가 4자리 이하인 경우 error를 출력 / 4자리 이상일 때 정상적으로 출력.

교육 간 사용할 경로: C:\yjcho\Node.js\Project\Server\middleware\validator.js

 


import {validationResult} from 'express-validator';

export const validate = (req, res, next) =>                // validate라는 이름의 middleware 함수를 정의함.
    const errors = validationResult(req); 
// validationResult 객체를 사용하여 이전 middleware에서 유효성 검사를 수행한 결과를 가져옴. 이 결과는 errors 변수에 저장됨.
    if(errors.isEmpty()){   // errors 객체가 비어있으면(유효성 검사를 통과), next() 함수를 호출하여 다음 middleware로 이동.
        return next();
    }
    return res.status(400).json({message: errors.array()});
// errors 객체가 비어있지 않으면(유효성 검사를 통과하지 못함)
res.status(400)을 사용하여 400 Bad Request 상태 코드를 반환하고, JSON 형식으로 errors 객체에 저장된 오류 메시지를 반환.
};

 

C:\yjcho\Node.js\Project\Server\router/tweets.js 경로에서 추가로 아래 코드를 작성.

 


import express from 'express';
import * as tweetController from '../controller/tweet.js';
import {body} from 'express-validator';
import {validate} from '../middleware/validator.js';    // 위의 칸에서 작성한 파일 validator.js를 가져옴.

const router = express.Router();
const validateTweet = [
    body('text')
        .trim()
        .isLength({min:4})    // text의 공백을 모두 제거하여 그 자릿 수가 4자리 이하인 경우 msg에러를 출력.
        .withMessage('text는 최소 4자 이상입니다.'),
    validate 
]
const app = express();

app.use(express.json());  //  Express 애플리케이션에 JSON 데이터를 파싱하기 위한 미들웨어 추가.

// POST 와 PUT에 유효성검사 매개변수를 이용.
router.post('/', validateTweet , tweetController.createTweet);
router.put('/:id', validateTweet , tweetController.updateTweet);

export default router;

 

Postman 프로그램을 작동해서 Body > Raw에 내용을 기재한 후 Save + Send 시 정상적으로 작동 확인.

POST 정상적으로 작동 시 추가한 내용이 정상적으로 생성.

 

 

text의 글자가 4글자 이하일 시, msg에 'text는 최소 4자 이상입니다.' 라는 문구 출력 후 error 발생.

 

 

 

5. 중간 점검

Client/Server 디렉토리 內 package.json 설치 후 터미널 2개를 띄워 실행.

 

1. C:\yjcho\Node.js\Project\Server 경로로 index.js 실행.

 

2. C:\yjcho\Node.js\Project\Client 경로로 react-script 실행.

 

정상적으로 twitter 기능을 담은 웹페이지 화면