웹훅 수신 엔드포인트를 body-parser 안 쓰고 req.on('data')로 직접 청크 모아서 처리하고 있어요. 평소엔 잘 되는데 payload가 크거나 한글이 많으면 가끔 글자가 깨져서 들어옵니다. "결제완료"가 "결제완�"처럼 마지막 글자가 이상한 문자로 바뀌는 식이에요.
let data = ''
req.on('data', chunk => { data += chunk.toString('utf8') })
req.on('end', () => {
const body = JSON.parse(data) // 가끔 여기서 SyntaxError
})payload가 작으면 재현이 안 되고 커질 때만 터져서 한참 헤맸습니다. 인코딩을 utf8로 명시했는데 대체 왜 깨질까요?
댓글 4
해결은 문자열로 이어붙이지 말고 Buffer로 모은 다음 마지막에 딱 한 번만 디코딩하는 겁니다.
const chunks = []
req.on('data', c => chunks.push(c))
req.on('end', () => { const body = JSON.parse(Buffer.concat(chunks).toString('utf8')) })
이러면 바이트를 온전히 다 모은 뒤에 디코딩하니 경계에서 안 잘려요.
만약 스트림을 계속 흘려보내면서 청크 단위 디코딩이 꼭 필요하면 string_decoder 모듈의 StringDecoder를 쓰세요. new StringDecoder('utf8') 만들고 decoder.write(chunk) 하면, 불완전한 멀티바이트는 내부 버퍼에 물고 있다가 다음 청크랑 합쳐서 내보내줍니다. 그게 정확히 이 문제 풀라고 있는 물건이에요.