ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Node.js (16) 프로토콜이해하기(2)&메서드 연습하기
    Node.js 2022. 12. 10. 17:43
    728x90

    메시지 받아보기

    메시지를 이제 받았다면 우리는 이제 바디 부분만 받아서 나타내고 싶을 수 있다

    그러면 이제 어떻게 그것을 받아볼까 이것도 expree로 구현하면 쉬울 수 있지만

    조금 원리를 들어가보자.

    const message = `reply:true
    content-type:string
    content-length:16
    
    답장주세요!`

    다음과 같이 우리가 메시지를 받았다면 메시지를 구분할 수 있는 조건은 무엇일까?

    바로 저 개행(\r\n)이다.

    윗 부분을 헤더

    const message = `reply:true
    content-type:string
    content-length:16
    `
    
    답장주세요!

    바디라고 구분하자.

    쪼개보자

    const message = `reply:true
    content-type:string
    content-length:16
    
    답장주세요!`
    
    console.log(message.split("\n"))
    
    [
      'reply:true',
      'content-type:string',
      'content-length:16',
      '',
      '답장주세요!'
    ]

    이렇게 결과 값이 나오게 된다.

    자 그럼 필터를 통해서 저 빈칸을 없애보자.

    const arr = message.split("\n").filter((v) => v !== "")

    다음과 같이 작성하면 빈칸이 사라지게 된다.

    그리고 답장주세요가 마지막에 있기 떄문에 저 답장주세요를 출력할 수 있는 함수를 다음과 같이

    작성해보자.

    const arr = message.split("\n").filter((v) => v !== "")
    console.log(arr.pop())
    console.log(arr)

    이렇게 작성한다면 문제가 해결된다.

    pop() 함수는 실제로 배열에서 마지막요소를 제거하고, 그것을 뽑아낸다.

    자 그럼 이제 이 친구들은 객체로 만들어야 한다.

    [ 'reply:true', 'content-type:string', 'content-length:16' ]

    여기서 가장 악명높은 reduce()가 등장하게 된다.

    reduce는 배열에서 다른 타입으로 변경할때 사용되고

    주로 교수님은 배열에서 객체로 바꿀 수 있다.

    reduce는 인자값을 두개를 받는다

    1. callback 함수
      하지만 이제 콜백안의 요소들이 필터나 맵보다 많이 난해하다
      1. 이전값
      2. 현재 요소들(filter, map에서 v로 적었던 것들)
      3. 인덱스
      4. 배열 전체 값 -> 거의 쓸일 없긴함
    2. 초기값: 여기서는 객체

    다음과 같이 작성해보자.

    const arr = message.split("\n").filter((v) => v !== "")
    const body = arr.pop()
    const headers = arr
    
    // 인자값
    headers.reduce((acc, value) => {
        console.log(acc, value)
    }, {})

    근데 결과값이 다음과 같이 나타난다.

    {} reply:true
    undefined content-type:string
    undefined content-length:16

    반복을 세번하긴 했다. 근데 다음부터 acc값이 undefined 값이 뜨게 되었는데

    그 이유는 return이 없기 떄문이다.

    return은 기본적으로 반환을 이전값을 반환한다.

    따라서 우리는 현재 가지고 있는 값들을 통해서 키와 밸류를 지정해

    빈 객체의 프로퍼티를 생성해준다면 결과값을 우리가 원하는 객체로 만들 수 있을것이다.

    const header = headers.reduce((acc, value) => {
        const [key, val] = value.split(":") //[reply,true])
        acc[key] = val
        return acc
    }, {})

    이때 구조분해 할당문중 배열구조분해 할당문은 자기가 쓰고 싶은 변수명을 쓰고

    결과를 도출해 낸다면 좀 더 깔끔하게 쓸 수 있다는 장점이 있다.

    이런식으로 만들어주면 첫번째 배열부터 끝까지 reduce가 작동하기 때문에

    우리가 원하는 값을 만들어 낼 수 있다.

    자 그럼 exports 하기 위해 다음과 같이 사용해야 하는데

    const result = (message) => {
        const arr = message.split("\n").filter((v) => v !== "")
        const body = arr.pop()
        const headers = arr
        const header = headers.reduce((acc, value) => {
            const [key, val] = value.split(":") //[reply,true])
            acc[key] = val
            return acc
        }, {})
        return {
            header,
            body
        }
    }

    자 이렇게 함수를 작성해주면 우리가 exports할떄 편하다.

    근데 이떄 중요한것은 header의 리턴 부분과 우리가 만드는 결과 함수의 리턴값이

    각자 설정해 주어야 한다는 것을 까먹어서는 안된다.

    또한 es6문법인데 객체의 key와 value가 같다면 생략해서 적어줄 수 있다는 점도 기억하자.

    자 다음에는 exports를 해보자

    module.exports= (message) => {
        const arr = message.split("\n").filter((v) => v !== "")
        const body = arr.pop()
        const headers = arr
        const header = headers.reduce((acc, value) => {
            const [key, val] = value.split(":") //[reply,true])
            acc[key] = val
            return acc
        }, {})
        return {
            header
            body
        }
    }

    객체를 내보내는 것이 아니라 함수를 내보낸 다는것을 기억하자.

    그런데 여기서 이 메세지와 parse는 같은 기능을 구현하므로

    message라는 부분을 따로 만들어 거기에 한꺼번에 exports해보자.

    const incodeMessage = (obj) => {
        obj['content-length'] = Buffer.from(obj.body).length
    
        const keys = Object.keys(obj)
        let arr = keys.filter(v => v !== "body").map(v => `${v}:${obj[v]}`).join("\r\n")
        arr += `\r\n\r\n${obj.body}`
    
        return arr
    }
    
    const decodeMessage = (message) => {
        const arr = message.split("\r\n").filter((v) => v !== "")
        const body = arr.pop()
        const headers = arr
        const header = headers.reduce((acc, value) => {
            const [key, val] = value.split(":") //[reply,true])
            acc[key] = val
            return acc
        }, {})
        return {
            header,
            body
        }
    }
    
    module.exports = {
        incodeMessage,
        decodeMessage
    }

    자 이런식으로 한 뒤에

    server에서 다시 한번 받아보자.

    server, client 를 한번 받아보자.

    const net = require("net")
    const { decodeMessage } = require("./lib/message")
    const port = process.env.SERVER_PORT || 3000
    const host = process.env.SERVER_HOST || "127.0.0.1"
    
    const server = net.createServer((client) => {
        client.setEncoding("utf8")
    
        client.on("data", (chunk) => {
            console.log(chunk) // obj-> string을 했지만 이제는 string -> obj
            const data = decodeMessage(chunk)
            console.log(data)
        })
    })
    
    server.listen(port, host, () => {
        console.log('server start')
    })
    server.on("connection", () => {
        console.log("connected to client")
    })

    client

    const net = require("net")
    const { incodeMessage: requestMessage, decodeMessage: b } = require("./lib/message")
    
    const config = { port: 3000, host: "127.0.0.1" }
    const socket = net.connect(config)
    
    socket.on("connect", () => {
        console.log('connected to server')
        const message = { reply: true, "content-type": "string", body: "답장주세요!" }
        socket.write(requestMessage(message))
    })

    이런식으로 받는다면 한번에 받을 수 있을 것이다.

    자 이제 우리는 원리에 대해서 공부해 보았다

    다음 시간에는 responseMessage에 대해서 공부해보자.

    댓글

Designed by Tistory.