2021-12-14 BE


ブログのコードソース
Node.js教科書改訂2版(知音趙賢英)

復習する


ノード機能


≪イベント|Events|ldap≫

myEvent.onおよびmyEvent.addListenerは、同じイベントリスナー登録キーワードである.
const EventEmitter = require('events')
const myEvent = new EventEmitter()

myEvent.on('event1',() => {
  console.log('이벤트1 발생!')
})

myEvent.on('event1', () => {
  console.log('이벤트 1 추가 발생')
})

myEvent.once('event2', () => {
  //이벤트를 한번만 발생하게 하는 .once 키워드
  console.log('이벤트 2 발생')
})

myEvent.on('event3', () => {
  console.log('event3 발생!!!')
})

myEvent.on('event3', () => {
  console.log('event3 추가 발생!!!')
})

const listner = ('event4',() => {
  console.log('event4 발!생!')
})
myEvent.on('event4',listner)

myEvent.emit('event1') //이벤트 발생기
myEvent.emit('event2')
myEvent.emit('event2')
myEvent.emit('event3')
myEvent.emit('event4')

myEvent.removeListener('event4',listner) //내가 추했던 listner를 삭제 가능하다.
myEvent.emit('event4')

myEvent.removeAllListeners('event3') // event3에 대한 모든 listner가 삭제된다.
myEvent.emit('event3')

// myEvent.addListener myEvent.on과 동일하다.
以下の関数形式で作成し、listner名を修正して使用した方が便利です.
const listner = ('event4',() => {
  console.log('event4 발!생!')
})
myEvent.on('event4',listner)
追加したlistnerを削除できます.
下列のemitは反応しなかった.
myEvent.removeListener('event4',listner) //내가 추가했던 listner를 삭제 가능하다.
myEvent.emit('event4')
.removeAllListenersを使用して、すべてのイベントlistnerを削除できます.
myEvent.removeAllListeners('event3') // event3에 대한 모든 listner가 삭제된다.
myEvent.emit('event3')
listnerを関数化します.
const listner = () => {
console.log(「event 4発!生!」)
}
myEvent.on('event4',listner)
匿名関数(コールバック形式)のみが使用されます.
myEvent.on('event3', () => {
console.log(「イベント3を追加!!!」
})
次のlistnerCount()を使用して、接続されたイベントの数を把握します.
console.log(myEvent.listenerCount('event3'))

例外処理


強制的にサーバエラーが発生しました.
let i = 0
console.log('시작')

setInterval(() => {
  if(i===5) {
    throw new Error('서버를 고장내주마!')
  } else {
    console.log(i)
    i++
  }
}, 1000);

try、catchを使用して異常を処理

trycatch構文を使用して例外処理を行います.
私はtryで私の予想した間違いをかばった.写真のように、サーバーは閉じずに回転し続けます.
let i = 0
console.log('시작')

setInterval(() => {
  try {
    if(i===5) {
      throw new Error('서버를 고장내주마!')
    } else {
      console.log(i)
      i++
    }
  } catch(err) {
    console.error(err)
  }
}, 1000);

内部モジュール機能による異常処理

// let i = 0
// console.log('시작')

// setInterval(() => {
//   try {
//     if(i===5) {
//       throw new Error('서버를 고장내주마!')
//     } else {
//       console.log(i)
//       i++
//     }
//   } catch(err) {
//     console.error(err)
//   }
// }, 1000);

const fs = require('fs')

setInterval(() => {
  fs.unlink('./abc.js', (err) => {
    //두 번째 인자에 콜백으로 err처리를 하면 예외처리를 지원하는 함수도 있다.
    //내부 처리임
    //이런 기능을 모르면 try,catch를 사용해도 괜찮음
    if(err) {
      console.log(err)![](https://media.vlpt.us/images/ansunny1170/post/2e01ffdf-845c-4105-932e-68dbfd99a919/image.png)
    }
  })
},1000)

内部モジュール+承諾の例外処理

const fs = require('fs').promises

setInterval(() => {
  fs.unlink('./abc.js').catch(err => {
    console.error(err)
  })
}, 1000);

エラーが発生した場合に例外を処理するかどうか分からない


これは推奨方法ではありませんが、誤った位置が特定しにくい場合に使用します.
最後の手段として使うのは、使わないほどいい.
process.on('uncaughtException',(err) => {
  console.error('예기치 못한 에러', err)
})

setInterval(() => {
  throw new Error('서버를 고장 낼꺼야')
}, 1000);
setInterval(() => {
  console.log('정상진행')
}, 1000);

よくあるエラーp.165


名学習


httpモジュールを使用したサーバの作成


リクエストとレスポンスの理解


http.createServer(res,res)


http.createServer((request, response) =>{ })サーバの作成方法
const http = require('http')

http.createServer((request,response) => {
  response.writeHead(200,{'Content-Type' : 'text/html;charset=utf-8'})
  //객체 형태로 기입하고 내용은 html형태로 받겠다라는 뜻이다. 그리고 utf-8형식으로 받겠다.
  response.write('<h1> hello node! </h1>')
  response.end('<p> hello server! </p>')
  //end를 꼭 써야 응답을 끝낼 수 있다. 끝내면서 content를 또 전달 할 수 있다.
})

.listen(3000, () => {
  console.log('포트 3,000번으로 실행하고 있음!')
})

他の人のIPでアクセスすることもできます.
cmdにipconfigを入力し、IPv 4でWIFIアドレスを確認できます.

http.createServerの実践


イベントリスナー方式

const http = require('http')

const server = http.createServer((request,response) => {
  response.writeHead(200,{'Content-Type' : 'text/html;charset=utf-8'})
  response.write('<h1> hello node! </h1>')
  response.end('<p> hello server! 김예찬! </p>')
})

server.listen(3000)
server.on('listening',() => {
  console.log('이벤트 리스너 방식으로 포트 3000번 포트에서 실행하고 있음')
})
server.on('error',(error) => {
  console.error(error)
})

複数のサーバを同時に開く

const http = require('http')

const server = http.createServer((request,response) => {
  response.writeHead(200,{'Content-Type' : 'text/html;charset=utf-8'})
  response.write('<h1> hello node! </h1>')
  response.end('<p> hello server! 김예찬! </p>')
})

server.listen(3000)
server.on('listening',() => {
  console.log('이벤트 리스너 방식으로 포트 3000번 포트에서 실행하고 있음')
})
server.on('error',(error) => {
  console.error(error)
})
//server1 = 두 번째 서버
const server1 = http.createServer((request,response) => {
  response.writeHead(200,{'Content-Type' : 'text/html;charset=utf-8'})
  response.write('<h1> hello node1! </h1>')
  response.end('<p> hello server1! 김예찬! </p>')
})

server1.listen(3001)
server1.on('listening',() => {
  console.log('이벤트 리스너 방식으로 포트 3001번 포트에서 실행하고 있음')
})
server1.on('error',(error) => {
  console.error(error)
})

htmlファイルを読み込んでサーバの作成を容易にする()

<html>
  <head>
    <meta charset="utf-8">
    <title>Nodejs 웹서버</title>
  </head>
  <body>
    <h1> 만들준비 되었나?</h1>
    <p> server 만들어 보자</p>
  </body>
  </html>
//async-await 방식
const http = require('http')
const fs = require('fs').promises

const server = http.createServer(async (req,res) => {
  try{
    const data = await fs.readFile('./index.html')
    res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'})
    res.end(data)
  } catch (err) {
    console.error(err)
  }
})

server.listen(3000)

server.on('listening', () => {
  console.log('서버 3000번으로 실행하고 있음')
})


htmlファイルを読み込み、サーバ()を作成します。then

//.then 방식
const http = require('http')
const fs = require('fs').promises

const server = http.createServer((req,res) => {
  const html = fs.readFile('./index.html')
  html.then((data) => {
    res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'})
    res.end(data)
  }).catch((err) => {
    console.error(err)
  })
})

server.listen(3001)

server.on('listening', () => {
  console.log('서버 3001번으로 실행하고 있음')
})

サーバに障害が発生した場合は、サーバをクライアントに送信し、サーバがハングアップすることはありません。


このようにエラーメッセージを送信することは、例外処理またはエラー処理とも呼ばれる.
//async-await 방식
const http = require('http')
const fs = require('fs').promises

const server = http.createServer(async (req,res) => {
  try{
    throw new Error('에러메시지 : 표시할 수 없는 화면입니다.') // 에러를 강제로 내보자
    //에러를 강제로 내면 catch에 잡힌다. 위에 에러메시지가  catch(err)의 err로 전달이 된다.
    const data = await fs.readFile('./index.html')
    res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'})
    res.end(data)
  } catch (err) {
    console.error(err)
    res.writeHead(500,{'Content-Type':'text/html; charset=utf-8'})
    res.end(`<p>${err.message}</p>`)
    //500 : 앞 인자는 응답코드다. 500번대는 서버 오류.
    //오류 발생 시, 사용자에게 오류상황을 알려줘야한다.
    //그럼 강제적으로 오류 발생을 어떻게 시킬까? 맨윗출 throw new Error이다.
  }
})

server.listen(3000)

server.on('listening', () => {
  console.log('서버 3000번으로 실행하고 있음')
})

RESTとは?


Representation State Transferの略
  • 要求方法:GET、POST、PUT、PATCH、DELETE、OPTIONS
  • GET:サーバリソースを取得します.要求本文にデータが含まれていません.サーバにデータを送信する必要がある場合は、クエリーを使用します.
  • POST:サーバに新しいリソースを登録します.新しく登録したデータをリクエストの本文に入れます.
  • PUT:サーバ上のリソースを要求中のリソースに変換するために使用されます.要求された本文に置換するデータを追加します.△すべての内容を変更する必要がある場合があるので、不便です.
  • PATCH:サーバリソースの一部のみを変更できます.要求された本文に修正が必要なデータを追加します.
  • DELETE:サーバ上のリソースを削除します.要求本文にデータが含まれていません.
  • OPTIONS:要求が発行される前に通信オプションを説明するために使用されます.12章によく登場します.
  • restServer.jsコアコード


    コード的にはreq.methodでHTTPリクエストメソッドを区別する.
    方法がGETである場合、要求アドレスはreq.urlに再分割される.
    アドレスが/の場合、restFront.htmlが提供されます.
    アドレスが/aboutである場合、about.htmlファイルが提供される.
    その他の場合は、アドレスのファイルを提供します./restFront.jsの場合、restFront.jsのファイルを提供します./restFront.cssの場合、restFront.cssファイルが提供されます.
    const http = require('http');
    const fs = require('fs').promises;
    
    const users = {}; // 데이터 저장용 db를 안쓰니 임시용으로 사용
    
    http.createServer(async (req, res) => {
      try {
        console.log(req.method, req.url);
        if (req.method === 'GET') { //GET요청이 왔을 때, 아래 3가지 요청이 있는 것이다.
          if (req.url === '/') { // '/'는 라우터라고 하며,
            const data = await fs.readFile('./restFront.html');
            res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
            return res.end(data); //.end에 return을 붙여준 이유 p.184
          } else if (req.url === '/about') {
            const data = await fs.readFile('./about.html'); //about.html을 보여줘라!
            res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
            return res.end(data);
          } else if (req.url === '/users') {
            res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
            return res.end(JSON.stringify(users)); //users는 현제 객체 형태이다.
            //JSON형식이니 문자형으로 바꿔줘!
          }
          // /도 /about도 /users도 아니면
          try {
            const data = await fs.readFile(`.${req.url}`);
            return res.end(data);
          } catch (err) {
            // 주소에 해당하는 라우트를 못 찾았다는 404 Not Found error 발생
          }
    他のHTTPリクエストメソッドを追加し、usersというオブジェクトをデータベースの代わりに宣言してユーザー情報を格納します.POST /userリクエストには新しいユーザが保存され、PUT /user/아이디リクエストにはそのユーザ名のユーザデータが変更された.DELETE /user/아이디は、ユーザ名を削除するユーザを要求する.
    POSTとPUTリクエストを処理すると、特別なものが見えます.req.on('data')req.on('end')の使用です.
    これは要求本文中のデータを抽出するためと考えられる.
    reqおよびres内部もストリーム(readStreamおよびwriteStream)であるため、リクエスト/応答のデータはストリームとして渡される.
    また,onで見ることができ,アクティビティもこれに依存する.
    受信したデータは文字列であるため、JSONのJSONとして作成される.解析プロセスが必要です.
        } else if (req.method === 'POST') {
          if (req.url === '/user') {
            let body = '';
            // 요청의 body를 stream 형식으로 받음
            req.on('data', (data) => {
              body += data;
            });
            // 요청의 body를 다 받은 후 실행됨
            return req.on('end', () => {
              console.log('POST 본문(Body):', body); //우측 body에 최종적으로 들어온다.
              const { name } = JSON.parse(body); //좌측 name을 찾아서 구조분해할당을 했다.
              const id = Date.now();
              users[id] = name; //키밸류형식으로 등록하였다.
              res.writeHead(201);
              res.end('등록 성공');
            });
          }
        } else if (req.method === 'PUT') {
          if (req.url.startsWith('/user/')) {
          //start가 user/라고 되어있으니 user/1 도 속한다.
          //req.url.startsWith -> 문자열.startsWith
            const key = req.url.split('/')[2]; 
            //문자열 기능중에 split기능이 있다. 구분인자는 `/`, [2]는 인덱스 호출번호.
            // ` /user/1`을 split하면 ' ', 'user', '1' 3가지 인자가 배열에 들어간다.
            //이러한 것들을 key값에 넣어주겠다라는 뜻이다. timestamp로 들어간다.
            let body = '';
            req.on('data', (data) => {
              body += data;
            });
            return req.on('end', () => {
              console.log('PUT 본문(Body):', body); //우측 body에 데이터가 다 담겼다.
              users[key] = JSON.parse(body).name;
              //위에서 timestamp로 받은 값에 대한 users배열을 수정하게 되겠다.
              return res.end(JSON.stringify(users));
            });
          }
        } else if (req.method === 'DELETE') {
          if (req.url.startsWith('/user/')) {
            const key = req.url.split('/')[2];
            delete users[key]; //object안의 원하는 item을 삭제하고 싶다면!!
            return res.end(JSON.stringify(users));
          }
        }
        res.writeHead(404);
        return res.end('NOT FOUND');
      } catch (err) {
        console.error(err);
        res.writeHead(500);
        res.end(err);
      }
     })
      .listen(8082, () => {
        console.log('8082번 포트에서 서버 대기 중입니다');
      });
  • クライアントHTTP要求
  • ※重要!コアコードを確認してコメントを練習する必要があります

    const http = require('http')
    const fs = require('fs').promises
    
    const users = {} // 데이터 저장용
    
    const server = http.createServer(async(req,res) => {
      try {
        if (req.method === 'GET') { //GET메소드로 들어 왔다면!
          if(req.url === '/'){ //url이 기본 url로 들어 왔다면! 첫 화면을 뜻함
            const data = await fs.readFile('./restFront.html') //그러면 해당 html읽어서 보여줄께! 
            res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'})// writeHead가 없다면 위 html파일이 평문으로 들어간다!
            return res.end(data)
          } else if (req.url === '/about') {
            const data = await fs. readFile('./about.html') //about 버튼을 눌러보자
            res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
            return res.end(data)  
          } else if (req.url === '/users') {
            res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' })
            return res.end(JSON.stringify(users)) //JSON으로 바꿔서 보내준다
          } 
          try { // ./restFront.css, restFront.js파일을 불러오면
            const data = await fs.readFile(`./${req.url}`) //메서드가 GET이면 다시 req.url로 요청 주소를 구분한다! //.then의파라미터에 해당하는 것이 await...이다. data가 곧 .then의 파라미터
            return res.end(data)
          } catch(err) {
            console.error(err)
          }
        } else if (req.method === 'POST') {
          if(req.url === '/user') {
            let body = '' //빈문자열을 만든다. string으로 받으려 한다. 무엇을?
    
            req.on('data',(data) => {
              body = body + data 
            })
    
            return req.on('end',()=> {
              console.log('POST 본문 (body) : ', body)
              const {name} = JSON.parse(body) //body에 담긴 부분이 JSON이고 parse(파싱)을 해줘서 object로 만들어서 넣는다. 객체화 한다.
              //JSON은 객체는 아니지만 형태는 객체처럼 문자열로 온다.
              const id = Date.now() //현제시간 timestamp 형태로 찍힌다.
              users[id] = name//프론트에서 name값을 받아서 백엔드로 준다. 그걸 가지고 users객체에 담을 것이다. 그때 키가 필요하고, 키는 현재시간과 
              //처음 user={}빈객체이고 위의 id가 123으로 들어왔고, name이 김예찬으로 들어왔따면 {123:김예찬}이렇게 저장된다.
              //users[id] id는 ''스트링 형식으로 들어간다.
              console.log(users)
              res.writeHead(201,{ 'Content-Type': 'text/plain; charset=utf-8' })
              res.end('ok')
            })
          }
        } else if (req.method === 'PUT') {
          if (req.url.startsWith('/user/')) { // /user/1333.adsdf 이렇게 하면 다된다. /user/로 시작만 하면
            // 주소 : 3000/user/123124123 이런식으로 요청이 들어온다. 그래서 split으로 처리한다.
            console.log(req.url) //rul이 어떻게 들어오는지 알 수 있다. 
            const key = req.url.split('/')[2] //'/' 기준으로 잘라준다. /user/123 => ['','user','123'] 문자열을 배열화 한다.
            console.log(key)
            let body =''
            req.on('data', (data) =>{ //data가 chunk로 온다?!
              body +=data
            })
            return req.on('end', ()=> {
              console.log('PUT 본문 : ',body)
              users[key] = JSON.parse(body).name
              res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8'})
              res.end('ok')
              // return res.end(JSON.stringify(users));
            })
          }
        } else if (req.method === 'DELETE') {
          if(req.url.startsWith('/user/')) {
            const key = req.url.split('/')[2]
            delete users[key]
            res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'})
            res.end('ok')
          }
        }
    
      } catch (err) { //err은 변수 이름으로 error해도 된다.
        console.error(err)
        res.writeHead(500,{'Content-Type': 'text/plain; charset=utf-8'})
        res.end(err.message)
      }
    })
    
    server.listen(3000)
    
    server.on('listening', () => {
      console.log('포트 3000번 사용중')
    })
    server.on('error', (err) => { //err은 변수 이름으로 error해도 된다.
      console.error(err)
    })

    Cookieとセッション


    Cookieチュートリアルp.190の設定

  • ログイン用、セッションの保持
    もしログインしたら、ログインに成功したと言って、サーバーでCookieをあげます...やってるみたい
    ログイン状態を維持します.しかもまだ期限が切れています.
  • 普通はheadに入れて送ります.
    const http = require('http');
    
    http.createServer((req, res) => {
      console.log(req.url, req.headers.cookie);
      res.writeHead(200, { 'Set-Cookie': 'mycookie=test' }); //여기에 쿠키 있음.
      res.end('Hello Cookie');
    })
      .listen(8083, () => {
        console.log('8083번 포트에서 서버 대기 중입니다!');
    });