💬 들어가며
최근 프로젝트를 하며 json
데이터를 DB
컬럼에 그대로 저장해야 할 상황이 많았다.
그동안 Nest.js
TypeORM
으로 json
데이터 타입을 다루는데 큰 문제가 없었는데multipart/form-data
로 넘어오는 json
데이터를 저장하며 문제가 발생했다.
개발 환경은 다음과 같다.
- Nest.js + Typescript
- TypeORM
- PostgreSQL
🔥 문제
DB
에 저장할 때 이런식으로 \n
, \
와 같은 개행문자가 들어가 있었다.
"[\n {\n \"index\": \"1\", ...생략]"
조회할 때도 정의된 Json
형식으로 매핑이 안되고 저 데이터 자체가 스트링으로 인식되어 통으로 매핑되었다.
- 쿼리 매핑 결과
{
"data": "[{\"index\": 1}, {\"index\": 2}]"
}
- 원했던 결과
{
"data": [
{
"index": 1
},
{
"index": 2
}
]
}
✔️ 원인
구글링을 열심히 했지만, 관련 자료가 많이 없었고
내 상황에는 다 적용이 되지 않아 이것저것 시도해보다가 방법을 찾았다.
insert 시 개행문자가 포함되는 이슈 - 발생 원인
우선 데이터 저장 시에 \n
이나 \
가 저장되는 것은 일반적인 application/json
상황에는 발생하지 않았고 multipart/form-data
로 넘어온 application/json
데이터 타입 상황에서만 발생했다.
뭔가 데이터 전송 방식에 따라 개행문자가 발생하는 것이 아닐까 생각한다.
select 시 json 객체로 매핑 안되는 이슈 - 발생 원인
multipart/form-data
가 아닌 application/json
으로 통신할 때는 조회 시에도 별 다른 문제가 없었다.
그런데 multipart/form-data
에서 파싱한 json
컬럼 데이터를 조회해 올 때는 문제가 발생했다.
(왜지? 어차피 DB에 들어가 있는 데이터를 조회하는데 네트워크 통신에서 발생하는 content-type
과 상관이 있는게 이상하다.)
💣 해결
Entity
컬럼 정의 - jsonb
@Entity()
export class Entity {
// ... 생략
@Column({ type: 'jsonb', nullable: false, default: [] })
data: object[]; // 그냥 object 했을 때도 문제 없었다.
}
type
은 json
이나 jsonb
모두 잘 동작한다.
💡 참고:
공식 문서에 따르면
json
,jsonb
두 타입의 차이는 다음과 같다.
Json Jsonb 데이터가 들어온 그대로 저장 문자열 사이의 공백 제거
(근데 나는 \ 엄청 들어오던데…)key 순서 보장 O key 순서 보장 X 인덱싱 불가능 인덱싱 가능 바이너리 포맷으로 저장됨
(바이너리 포맷이라 select 시에 json 매핑 이슈가 있었던 것일까…?)
예를 들면 아래와 같은 데이터가 있다고 할 때Json
은 순서가 그대로DB
에 들어가지만,Jsonb
는job
다음에name
이 저장되기도 한다.{ "name": "jinny" "job": "backend-developer" }
Service
- 조회 로직
- 처음에
JSON.parse(Json.stringify(entity.data))
도 시도했지만 매핑이 안되었고 아래 처럼 코드를 바꾸어줬더니 해결되었다.
async findJsonEntity() {
const entity: JsonEntity | null = await this.jsonEntityRepo.findOne({
where: { id: BigInt(1) },
});
if (!entity) {
throw new NotFoundException();
}
return { data: JSON.parse(entity.data as unknown as string) }; // 요렇게!!
}
👋 마치며
nest.js
는 구글링해도 정말 자료가 잘 안나와서, 영어로 구글링하는 것이 좋은 것 같다.
그나마 스택오버플로우에 질문을 보며 힌트를 얻을 때가 있는 것 같다.