[Node.js] chap.3

 

 

 

method-override

GET, POST, PUT, DELETE 중에서 나머지 사용할 수 있게 해줌

npm install method-override // REST API => 

 

- 세팅

// (server.js)
const methodOverride = require('method-override')
app.use(methodOverride('_method'))

 

<!-- (edit.ejs) -->

<form action="/add?_method=PUT" method="POST">
  <input 어쩌구>
</form>

해당 형식으로 작성하면 PUT 요청을 할 수 있다.

 

(server.js) 

app.put('/edit', function(요청, 결과){ 
  db.collection('post').updateOne( {_id : parseInt(요청.body.id) }, {$set : { 제목 : 요청.body.title , 날짜 : 요청.body.date }}, 
    function(){ 
    
    console.log('수정완료') 
    응답.redirect('/list') 
  }); 
});

 

 

** 서버의 요청(GET, POST, PUT, PATCH, DELETE)이 완료되고 난 후에, 응답이 없으면 브라우저가 멈출 수 있기 때문에 꼭 응답처리가 필요하다.

 

 

 

회원인증

1. Session-based Authentication

 - 사용자의 세션정보를 저장해서 로그인 기능을 구현

 

2. JWT (JSON Web Token)

 - 세션데이터를 서버에 저장하지않고 토큰을 사용자에게 주는 방식

 

3. OAuth (Open Authentication)

 - 소셜로그인

 

 

* Session-based Authentication

npm install passport passport-local express-session

실제 서비스시 express-session 말고 MongoDB에 세션데이터를 저장해주는 라이브러리를 이용하면 좋다.(ex. connect-mongo)

 

// (server.js)
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const session = require('express-session');

app.use(session({secret : '비밀코드', resave : true, saveUninitialized: false}));
app.use(passport.initialize());
app.use(passport.session());

:라이브러리에 나와있는 사용법 그대로

 

 

- 미들웨어

app.use()와 같은 부분을 미들웨어라고 하는데 요청과 응답 사이에 뭔가를 실행시키는 코드라고 생각하면 된다. 예를 들어 요청이 적법한지 검사하는 것이다. (passport.intialize(), passport.session() 등)

 

 

- 로그인 검사

app.post('/login', passport.authenticate('local', {failureRedirect : '/fail'}), function(요청, 응답){
  응답.redirect('/')
});

post() 함수의 두번째 파라미터로 passport 라이브러리가 제공하는 인증하는 코드이다. (failureRedirect 부분은 로그인 실패시 이동시켜줄 경로이다.)

 

 

* passport

passport.use( new LocalStrategy({ 
    usernameField: 'id', // (요기는 사용자가 제출한 아이디가 어디 적혔는지) 
    passwordField: 'pw', // (요기는 사용자가 제출한 비번이 어디 적혔는지) 
    session: true, // (요기는 세션을 만들건지) 
    passReqToCallback: false, // (요기는 아이디/비번말고 다른 정보검사가 필요한지) 
});

 

- passport 라이브러리 설정코드

// (server.js 하단에 복붙)

passport.use(new LocalStrategy({
  usernameField: 'id',
  passwordField: 'pw',
  session: true,
  passReqToCallback: false,
}, function (입력한아이디, 입력한비번, done) {
  //console.log(입력한아이디, 입력한비번);
  db.collection('login').findOne({ id: 입력한아이디 }, function (에러, 결과) {
    if (에러) return done(에러)

    if (!결과) return done(null, false, { message: '존재하지않는 아이디요' })
    if (입력한비번 == 결과.pw) {
      return done(null, 결과)
    } else {
      return done(null, false, { message: '비번틀렸어요' })
    }
  })
}));

: LocalStrategy( { 설정 }, function(){ 아이디비번 검사하는 코드 } )

 

 

- 세션 생성 및 세션 아이디 쿠키로 보내기

passport.serializeUser(function (user, done) {
  done(null, user.id)
});

passport.deserializeUser(function (아이디, done) {
  done(null, {}) // 추후 수정
});

serializeUser(): 유저의 id 데이터를 바탕으로 세션데이터를 만듦. 그 세션데이터의 아이디를 쿠키로 만들어서 사용자의 브라우저로 보냄.

deserializeUser(): 세션아이디를 바탕으로 이 유저의 정보를 DB에서 찾는역할을 한다.

 

 

// (server.js) 

app.get('/mypage', loginCheck, function (req, res) { 
  console.log(req.user); 
  res.render('mypage.ejs', {}) // 추후 수정 
}) 

function loginCheck(req, res, next) { 
  if (req.user) { 
    next() 
  } 
  else { 
    res.send('로그인안하셨는데요?') 
  } 
}

1. get() 함수에 미들웨어 loginCheck()를 넣으면 /mypage 요청과 mypage.ejs 응답 사이에 loginCheck라는 코드를 실행시켜준다.

2. loginCheck()는 req.user가 있으면 next()로 통과시키고, 없으면 에러메세지를 res.send() 해달라는 뜻이다.

 

 

passport.deserializeUser(function (아이디, done) {
  db.collection('login').findOne({ id: 아이디 }, function (error, result) {
    done(null, result)
  })
});

{id : 세션아이디에 숨겨져있던 유저의 아이디} 인 게시물을 찾아서 그 데이터의 결과를 done(null, result)를 실행한다. 그러면 결과가 req.user 부분에 들어간다. (로그인한 유저의 DB 데이터를 볼 수 있다.) 로그아웃은 req.logout()으로 실행할 수 있다.

 

 

app.get('/mypage', loginCheck, function (req, res) {
  console.log(req.user);
  res.render('mypage.ejs', { 사용자: req.user })
})

deserializeUser()를 통해서 req.user 부분에 로그인한 사용자의 id를 통한 DB의 정보를 가져올 수 있기 때문에 mypage.ejs에서 로그인한 사용자의 정보를 화면에 출력할 수 있다.

 

 

 

.env (environment variable) 파일

- 환경에 따라 가변적인 변수 데이터를 모아 놓은 파일

npm install dotenv

- 라이브러리 설치

 

 

// (server.js)

require('dotenv').config()

- 설치한 라이브러리 등록

 

 

* .env 파일을 server.js 파일과 동일한 경로에 생성한다. *

// (.env 파일)

PORT=8080
DB_URL="mongodb+srv://test@test"

- .env 파일 작성법

 

 

// (기존 server.js 코드)

var db;
MongoClient.connect('mongodb+srv://test:test@test', function(err, client){
  if (err) return console.log(err)
  db = client.db('Example1');
  app.listen(8080, function() {
    console.log('listening on 8080')
  })
}) 


// (env 파일을 적용하는 server.js 코드)

var db;
  MongoClient.connect(process.env.DB_URL, function(err, client){
  if (err) return console.log(err)
  db = client.db('Example1');
  app.listen(process.env.PORT, function() {
    console.log('listening on 8080')
  })
})

.env 파일에 작성된 변수들은 process.env.변수명 의 형식으로 사용한다.

 

 

 

URL query string

url 입력하는 곳에 /example?key=value 형식으로 요청을 보내면 GET 요청으로 데이터를 보낼 수 있다.

 

 

<div class="container input-group mb-2">
  <input class="form-control" id="search-input">
  <button class="input-group-append btn btn-danger" id="search">검색</button>
</div>

<script>
  $('#search').click(function(){
    var 입력한값 = $('#search-input').val();
    window.location.replace('/search?value=' + 입력한값)
  });

</script>

window.location: 현재 해당하는 페이지의 URL을 뜻한다.

 

 

app.get('/search', (req, res)=>{
  console.log(req.query);
  db.collection('post').find({제목 : req.query.value}).toArray((error, result)=>{
    console.log(result)
  })
})

req.query: 요청받은 URL query string 값을 가져올 수 있다.

 

 

 

 

 

 

 

 

 

indexing

검색할 때, 해당 검색어를 포함한 여러 데이터를 가져오는 방식은

 

 

1. 정규식 사용

db.collection('post').find({제목 : /글쓰기/})

- 데이터가 대용량이면 성능 안나옴

 

 

2. 인덱싱 사용

Binary Search: (숫자가 정렬되어 있다는 전제하에 => indexing) 정해진 숫자의 처음 중간의 값을 선택하여 그 값과 찾는 값의 크고 작음을 비교하면서 탐색한다.

 

 

- query string 찾는 다른 방법

// jQuery

var 자료 = { 이름1 : '값1', 이름2 : '값2' }
$param(자료) //이름1=값1&이름2=값2 이거가 남음

$('form').serialize() // input에 name있는 모든 값

 

 

- MongoDB indexing

(해당)collection - indexes - create index

문자열: 'text'

숫자: 1 or -1

 

해당 작업을 하면 collection의 정렬된 사본을 만들어준다.

app.get('/search', (요청, 응답)=>{
  console.log(요청.query);
  db.collection('post').find( { $text : { $search: 요청.query.value }} ).toArray((에러, 결과)=>{
    console.log(결과)
    응답.render('search.ejs', {posts : 결과})
  })
})

 

 

이 방법으로 간단한 검색엔진처럼 검색이 가능하다. (ex. 단어 -단어, "단어 단어" 등)

단점: 띄어쓰기를 기준으로 검색이 안된다 (영어에 특화된 검색)

 

 

- 해결방법

1. 검색할 양을 제한한다.

 - 날짜별로 검색하거나, skip(), limit() 함수를 이용해서 페이징 처리를 한다.

 

2. text Search를 사용한다.

 - MongoDB Atlas말고 직접 설치한다. 띄어쓰기 단위 indexing이 아닌 글자 단위의 indexing 알고리즘을 사용할 수 있다. (nGram)

 

3. Search index를 사용한다.

 - MongoDB Atlas에서 Search index를 생성할 수 있다. text index랑 비슷하지만 lucene.korean으로 변경하면 한국어에 쓰기 좋게 조사 등을 제거하고 검색을 할 수 있다.

 

 

- Search index

app.get('/search', (req, res) => {
    var 검색조건 = [
        {
            $search: {
              index: 'titleSearch', // Search index에서 설정한 name
              text: {
                query: req.query.value,
                path: '제목'  // 제목날짜 둘다 찾고 싶으면 ['제목', '날짜']
              }
            }
        },
        // { $sort: { _id: 1 } }, // 오름차순 1, 내림차순 -1
        // { $limit: 10 } // 갯수 설정
        // { $project : { 제목: 1, _id: 1, score: { $meta: "searchScore" } } } // meta 부분은 검색되는 빈도 등의 점수를 기준으로 검색
    ]
    db.collection('post').aggregate(검색조건).toArray(function(error, result){ // find() 대체
        console.log(result);
        res.render('search.ejs', {posts: result});
    })
})

 

 

 

router

서버에 요청하는 수 많은 경로들을 분류하여 관리할 수 있다.

root/routes/파일.js

server.js와 나란한 경로에 routes라는 폴더를 생성하고 그 안에 js 파일을 생성한다.

 

 

// 라우팅하는 .js 파일
var router = require('express').Router(); // express의 Router기능

router.get('/shop/shirts', function(req, res){
   res.send('셔츠 파는 페이지입니다.');
});

router.get('/shop/pants', function(req, res){
   res.send('바지 파는 페이지입니다.');
}); 

module.exports = router; // import/export 대체 가능
app.use('/', require('./routes/shop.js') ); // 미들웨어 형식으로 라우터를 적용

 

 

- URL 단축

app.use('/shop', require('./routes/shop.js') );

/////////////////////////////////////////////

var router = require('express').Router();

router.get('/shop/shirts', function(req, res){
   res.send('셔츠 파는 페이지입니다.');
});

router.get('/shop/pants', function(req, res){
   res.send('바지 파는 페이지입니다.');
}); 

module.exports = router;

 

 

- 라우터에 미들웨어 적용

var router = require('express').Router();

// router.use(loginCheck); // 전역적으로 loginCheck라는 미들웨어 사용 가능
router.use('/shop/shirts', loginCheck); // '/shop/shirts'의 경로에 loginCheck라는 미들웨어 사용 가능

function loginCheck(req, res, next) {
  if (req.user) { next() }
  else { res.send('로그인 안하셨는데요?') }
}
router.get('/shop/shirts', function(req, res){
   res.send('셔츠 파는 페이지입니다.');
});

router.get('/shop/pants', function(req, res){
   res.send('바지 파는 페이지입니다.');
}); 

module.exports = router;

 

 

 

Google Cloud Platform 배포

1. app.yaml 파일 생성

runtime: nodejs 
env: flex

/////////////// 밑으로는 안적어도 자동 생성
manual_scaling:
  instances: 1
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

 

 

2. server.js 포트 확인

app.listen(8080, function() {
    console.log('listening on 8080')
})

- 구글클라우드 기본 포트가 8080. (.env 파일 세팅있다면 확인)

 

 

3. MongoDB Atlas Network Access 모든 아이피 허용 확인

- 모든 아이피 (0.0.0.0) 접속허용 (Allow Access from Anywhere 로 체크)

 

 

4. Google Cloud Platform 시작

 - 회원가입 및 카드등록 및 프로젝트 생성

 - 프로젝트 생성 후 App Engine에서 배포 진행

 - SDK 설치 진행

 

5. 배포

 

** 프로젝트가 설치되어 있는 경로로 이동한다.**

gcloud init

Google Cloud Platform 개설한 구글 아이디 로그인 및 프로젝트 선택

 

gcloud app deploy

작업이 끝나면 배포할 소스파일, 이름, url 등을 알려주고 배포 완료.

 

 

 

이미지 처리

이미지 업로드 및 이미지 서버를 만들 수 있다.

 

<form method="POST" action="/upload" enctype="multipart/form-data" > // multipart => 인코딩을 하지 않아 상대적으로 파일 용량 적게 가져옴
    <input type="file" name="profile">
    <button type="submit">전송</button>
</form>

: ejs

 

 

npm install multer // multipart

multipart/form-data 라이브러리 설치

 

 

let multer = require('multer');
// var storage = multer.memoryStorage({}) // memory 상에 저장 (휘발성)
var storage = multer.diskStorage({

  destination : function(req, file, cb){ // 이미지 저장 경로
    cb(null, './public/image')
  },
  filename : function(req, file, cb){ // 이미지 이름 설정
    cb(null, file.originalname )
  },
  fileFilter: function (req, file, callback) { // 이미지 저장 시 필터처리
        var ext = path.extname(file.originalname);
        if(ext !== '.png' && ext !== '.jpg' && ext !== '.jpeg') {
            return callback(new Error('PNG, JPG만 업로드하세요'))
        }
        callback(null, true)
    },
    limits:{
        fileSize: 1024 * 1024 // 이미지 사이즈 제한
    }
});

var upload = multer({storage : storage});

: (server.js) multer 세팅

 

 

// 이미지 저장 페이지
app.get('/upload', function(req, res){
  res.render('upload.ejs')
}); 

// 이미지 저장처리
app.post('/upload', upload.single('profile'), function(req, res){ // input태그의 name속성 'profile'
  res.send('업로드완료')
}); // upload 미들웨어처럼 실행

// 저장된 이미지 불러오기
app.get('/image/:imageName', function(req, res){
    res.sendFile(__dirname + '/public/image/' + req.params.imageName); // __dirname => 현재root경로
})

: server.js

 

 

 

서버와의 실시간 소통 (SSE)

1) 1초마다 서버에게 메세지를 요청한다.

2) 서버랑 유저간 지속적인 소통채널을 연다. 

 

2)의 경우가 서버의 부하가 덜 하고 get이나 post 등의 요청은 1회 요청이지만 지속적으로 서버에 응답을 하려할 때 사용한다.

// (server.js)
app.get('/message/:parentid', loginCheck, function(req, res){

  res.writeHead(200, {
    "Connection": "keep-alive",
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
  });

  res.write('event: test\n');
  res.write(`data: ${JSON.stringify(결과)}\n\n`);

});

1. 지속적 소통채널 개설을 하려면 서버는 res.writeHead() 처럼 쓴다.

2. 유저에게 계속 메세지를 보내고 싶을 때마다 res.write() 한다.

3. event: 이벤트명을 잘 작성하고 data: 전달할내용을 쓰면 된다.

 

 

// chat.ejs
var 지금누른채팅방id;
var eventSource;   //일단변수 

$('.list-group-item').click(function(){
  지금누른채팅방id = this.dataset.id;

  //프론트엔드에서 실시간 소통채널 여는법 
  eventSource = new EventSource('/message/' + 지금누른채팅방id);
  eventSource.addEventListener('test', function (e){
    console.log(JSON.parse(e.data));
  });

});

1. GET 요청 대신 new EventSource('/message/' + 지금누른채팅방id); 형태의 코드를 실행하면 서버에서 만들어놓은 실시간 채널에 입장 가능하다.

2. eventSource.addEventListener('서버에서작명한이벤트명') 이런 코드를 쓰면 서버가 보낸 데이터를 수신할 수 있다. 서버가 res.write() 할 때마다 내부 코드를 실행해줍니다.

3. e.data 안에는 서버가 보낸 데이터가 들어있다.

 

 

 

DB 변동사항 실시간 업데이트

* MongoDB change stream

- 데이터 베이스의 변동을 감지하여 서버에게 업데이트 사항을 알려준다.

// ejs
var 지금누른채팅방id;
var eventSource; 

$('.list-group-item').click(function(){
  지금누른채팅방id = this.dataset.id;
  $('.chat-content').html('') //일단 메세지들 전부 비우기

  eventSource = new EventSource('/message/' + 지금누른채팅방id);
  eventSource.addEventListener('test', function (e){
    console.log(e.data);

    var 가져온거 = JSON.parse(e.data);
    가져온거.forEach((a) => {
        $('.chat-content').append(`<li><span class="chat-box">${a.content}</span></li>`)
    });
  });

});

1. 가져온 데이터를 JSON -> Object 형태로 변환

2. Array로 되어있는 object 안에 하나하나 메세지 document 들을 반복문을 통해 하나씩 분리

3. 그거 안에 있던 메세지들을 <li></li> 태그로 만들어서 원하는 곳에 출력

 

 

// server
const 찾을문서 = [
	{ $match: { 'fullDocument.parent': 요청.params.parentid } } // collection의 변경 감지 부분
];

const changeStream = db.collection('message').watch(찾을문서);
	changeStream.on('change', result => {
		console.log(result.fullDocument);
		var 추가된문서 = [result.fullDocument]; // 화면에 반영할 부분
        응답.write('event: test\n');
		응답.write(`data: ${JSON.stringify(추가된문서)}\n\n`);
});

change stream을 이용해서 DB 감시 한다.

- 우선 { parent : 요청.params.parentid } 인 게시물들만 감시

- 그런 게시물들에 변동사항이 생기면 [result.fullDocument] 이걸 유저에게 보내줌

- 물론 [], {} 이런 자료들은 JSON으로 바꿔서 보내야한다.

 

 

 

 

 

 

 

 

 

socket.io

SSE(서버에서 일방적으로 실시간 응답) 말고도 서버와 유저간에 Web Socket을 통해 양방향 실시간 통신이 가능하다.

 

npm install socket.io

라이브러리 설치

 

// (server.js)
const http = require('http').createServer(app);
const { Server } = require("socket.io");
const io = new Server(http);

- const app = express()보다 하단에 적용

 

// 기존
app.listen(process.env.PORT, function(){
  console.log('listening on 8080')
}); 

// 변경
http.listen(process.env.PORT, function(){
  console.log('listening on 8080')
});

app: express를 이용해서 서버를 띄움

http: 기본 nodejs 라이브러리 + socket.io를 이용해서 서버를 띄움

 

 

- 웹소켓 연결

// (server.js)
app.get('/socket', function(요청,응답){
  응답.render('socket.ejs')
});

io.on('connection', function(){ // 웹소켓으로 서버에 connection했을 때
  console.log('연결되었어요');
  
  socket.on('user-send', function(data){ // user-send라는 이벤트 발생 시 data 실행
    console.log(data) // '안녕하세요'
  });
});

연결 됐을 때 할 작업

 

// ejs
<body>
<script src="https://cdn.socket.io/4.4.0/socket.io.min.js" integrity="sha384-1fOn6VtTq3PWwfsOrk45LnYcGosJwzMHv+Xh/Jx5303FVOXzEnw0EpLv30mtjmlj" crossorigin="anonymous"></script>

<div>채팅방</div>
<button id="send">서버에 메세지 보내기</button>

<script>
  var socket = io(); // 웹소켓을 이용해 서버와 실시간 소통채널 개설
  $('#send').click(function(){
    socket.emit('user-send', '안녕하세요') // (이벤트 명, 전달할 데이터)
})
</script>
</body>

- package.json에 있는 버전 맞춰서 cdn 설치

- emit: 'user-send'라는 이벤트 명으로 보낸 '안녕하세요'라는 데이터 수신

 

 

- 서버에서 클라이언트로 데이터 보내기

io.emit('작명', '보낼메세지');

모든 유저에게 메세지를 보낸다 (broadcast 한다)

 

// (server.js)
io.on('connection', function (socket) {
  socket.on('user-send', function (data) { // user-send 이벤트가 일어나면
    io.emit('broadcast', data)  //모든사람에게 데이터 전송
  });
});
// (chat.ejs) 
<script>
  var socket = io();
  $('#send').click(function(){
    socket.emit('user-send', '안녕하쇼')
  });
  socket.on('broadcast', function(data) {
      $('#content').append('<div>' + data + '</div>')
  });
</script>

 

- 내가 원하는 사람에게만 메세지보내기

io.on('connection', function(socket){
  console.log(socket); // header, id 등이 출력 됌
  io.to(socket.id).emit("broadcast", '서버응답임'); 원하는 소켓id를 가진 사람에게만 메세지 보냄
});

 

 

- 하위 채팅방 만들기

<!-- (socket.ejs) -->
<button id="room1">채팅방1 입장</button>
<button id="room1-send">채팅방1에 메세지 전송</button>

<script>
  $('#room1').click(function(){
    socket.emit('joinroom', '제발');
  });

  $('#room1-send').click(function(){
    socket.emit('room1-send', '어쩌구저쩌구' )
  });
  
</script>

- joinroom 이라는 이벤트를 발생시킨다.

- room1-send라는 이벤트를 발생시킨다.

 

// (server.js)
io.on('connection', function(socket){

  socket.on('joinroom', function(data){
    socket.join("room1"); // room1이라는 방에 유저를 넣을 수 있다.
  });

  socket.on('room1-send', function(data){
    io.to("room1").emit('broadcast', data);
  });
});

- joinroom이라는 이벤트가 발생되면 room1이라는 방을 만들고 유저를 넣는다.

- room1-send라는 이벤트가 발생되면 room1에 있는 사람들에게만 broadcast 해준다.

 

 

 

Node+Express & React 연동

1) npm init

2) npm install express

3) server.js 파일 생성

// (server.js)
const express = require('express');
const path = require('path');
const app = express();

const http = require('http').createServer(app);
http.listen(8080, function () {
  console.log('listening on 8080')
});

4. (node or nodemon) server.js

 

 

- react 연동

1) react 프로젝트 생성(npx create-react-app 프로젝트명)

2) react 프로젝트 경로로 들어가서 작업 후 npm run build 명령어로 build 파일 생성

// (server.js에 추가)
app.use(express.static(path.join(__dirname, 'react-project/build'))); // 특정 폴더안의 파일을 static 파일로 보내기 위함

app.get('/', function (요청, 응답) {
  응답.sendFile(path.join(__dirname, '/react-project/build/index.html'));
});

// 라우팅을 하기 위함
app.get('*', function (요청, 응답) {
  응답.sendFile(path.join(__dirname, '/react-project/build/index.html'));
});

 

 

- node.js와 react간 ajax요청 시 (여러 const 하단에 추가)

npm install cors --save // 에러날 시에
app.use(express.json());
var cors = require('cors');
app.use(cors());

 

 

- 서브디렉토리에 리액트앱 발행하는 경우

// (server.js)
app.use( '/', express.static( path.join(__dirname, 'public') ))
app.use( '/react', express.static( path.join(__dirname, 'react-project/build') ))

app.get('/', function(요청,응답){
  응답.sendFile( path.join(__dirname, 'public/main.html') )
}) 
app.get('/react', function(요청,응답){
  응답.sendFile( path.join(__dirname, 'react-project/build/index.html') )
})
// (리액트프로젝트 내의 package.json)
{
  "homepage": "/react", // 이 부분 설정
  "version": "0.1.0",
  ... 등
}

 

 

- 서버와 리액트 동시에 띄워서 개발

리액트의 localhost와 node.js서버의 localhost의 미리보기를 띄어놓고 개발을 진행하기 위함.

// (리액트프로젝트 내의 package.json)
{
  ...
  "proxy": "http://localhost:8080"
  ...
}

- 리액트의 package.json에서 proxy부분을 추가

https://create-react-app.dev/docs/proxying-api-requests-in-development/

 

Proxying API Requests in Development | Create React App

Note: this feature is available with react-scripts@0.2.3 and higher.

create-react-app.dev

 

** socket이 들어가면 추가적으로 수정 필요 **

 

 

 

 

 

'NodeJs' 카테고리의 다른 글

[Node.js] chap.2  (0) 2021.12.21
[Node.js] chap.1  (0) 2021.12.20

+ Recent posts