본문으로 건너뛰기

에러로그: Connection was forcibly closed by a peer

· 약 6분
arch-spatula

혹시나 했는데 역시나 겪은 문제입니다.

tl;dr : deno oak에서 delete의 response.bodynull로 고정되어 있습니다.

문제: Connection was forcibly closed by a peer

  1. 서버를 구동하고 삭제요청을 보내면 Connection was forcibly closed by a peer이라고 응답합니다.
  2. Connection was forcibly closed by a peer 응답을 받은 이후부터는 MongoDB Atlas에서는 다른 요청을 거부합니다.

시도: MongoAPI가 문제인가?

가설: MongoAPI 메서드에 문제가 있습니다.

import { config } from 'https://deno.land/x/dotenv@v3.2.2/mod.ts';
import CardRecord from '../model/cards.ts';

class MongoAPI {
async deleteCards($oid: string) {
try {
const result = await fetch(`${this.baseURL}/deleteOne`, {
...this.options,
body: JSON.stringify({
...this.cardBody,
filter: { _id: { $oid } },
}),
});
return result.json();
} catch (error) {
return error;
}
}
}

try {
const mongoAPI = MongoAPI.getInstance();
console.log(await mongoAPI.deleteCards('어떤 id'));
} catch (error) {
console.log(error);
}

MongoAPI에서 삭제 모듈를 독립적으로 실행했는데 문제가 없습니다.

갱신은 정상동작

async function updateCard(ctx: Context) {
const { request, response, cookies } = ctx;
const { id } = helpers.getQuery(ctx, { mergeParams: true });
try {
if (!request.hasBody) throw Error('No Data');

const jwt = await cookies.get('user');
if (!jwt) {
throw Error('인증이 안 되어 있습니다.');
}

const userId = await token.tokenToUserId(jwt);
if (!userId) throw Error('사용자 id가 없습니다.');

const { question, answer, submitDate, stackCount } = await request.body()
.value;
if (!question || !answer || !submitDate || !stackCount)
throw Error('question, answer, data, stackCount 중 값이 1개 없습니다.');

const card = new CardRecord(
question,
answer,
submitDate,
stackCount,
userId,
id
);

response.status = 200;
response.body = await mongoAPI.patchCards(card);
} catch (error) {
response.status = 400;
response.body = {
success: false,
msg: `${error}`,
};
}
}

갱신은 body가 필요한데 정상적으로 동작합니다.

하지만 delete는 body가 딱히 필요한 메서드는 아닙니다.

async function deleteCard(ctx: Context) {
const { response, cookies } = ctx;
const { id } = helpers.getQuery(ctx, { mergeParams: true });
try {
const jwt = await cookies.get('user');
if (!jwt) {
throw Error('인증이 안 되어 있습니다.');
}

const userId = await token.tokenToUserId(jwt);
if (!userId) throw Error('사용자 id가 없습니다.');

response.status = 204;
response.body = await mongoAPI.deleteCards(id);
} catch (error) {
response.status = 400;
response.body = {
success: false,
msg: `${error}`,
};
}
}

가설: DB 서버 보안문제

가설: 보안문제로 특정 요청에 대해서 설정을 풀어줘야 한다.

하지만 DB 서버에서 보안문제가 있었으면 독립적인 모듈로 통신할 때부터 요청 거절이 되어야 합니다. 하지만 문제가 없었습니다.

해결: Deno oak에서 delete의 response body는 null로 고정되어 있습니다.

async function deleteCard(ctx: Context) {
const { response, cookies } = ctx;
const { id } = helpers.getQuery(ctx, { mergeParams: true });
try {
const jwt = await cookies.get('user');
if (!jwt) {
throw Error('인증이 안 되어 있습니다.');
}

response.status = 204;
const msg = await mongoAPI.deleteCards(id);
console.log(msg);
response.body = null;
} catch (error) {
response.status = 400;
response.body = {
success: false,
msg: `${error}`,
};
}
}

이렇게 작성하니까 해결되었습니다. 즉 response.body = null로 작성하면 삭제 구현이 됩니다.

학습: deno oak에서 delete의 response.bodynull로 고정되어 있습니다.

  1. deno oak에서 delete의 response.bodynull로 고정되어 있습니다.
  2. 디버깅할 때는 기록하면서 가설을 잘 설정하면 효율적입니다. 무엇이 아닌지 소거할 수 있습니다. 고급스러운 표현을 사용하면 버그 발원지의 면적을 축소할 수 있습니다.
  3. 에러메시지는 첫줄에 항상 주의합시다.

[uncaught application error]: TypeError - Response with null body status cannot have body

이런 메시지를 봤는데 나중에 발견했습니다.

백엔드 엔지니어의 고역

파일을 변경하고 API를 메뉴얼 테스트할 때 cookie를 새로 만들어야 한다는 백엔드 엔지니어의 고통을 공감할 수 있었습니다. 요청응답 시간이 3초만 넘겨도 엄청난 세월이 흐른 느낌이었습니다.

다른 고역도 있습니다. 테스트 코드 작성입니다. Deno를 테스트하는 방법이 익숙하지 않아서 그런 것 같습니다. 익숙해지면 쉽게 해결할 수 있을 것 같습니다. 일부 동작하는 테스트도 있지만 일부 작성에 실패한 테스트도 있습니다. 다른 프로젝트 진행하면서 학습할 수 있을 것 같습니다.