Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 티스토리챌린지
- 공부
- kotlin coroutine
- AWS EKS
- 기록으로 실력을 쌓자
- 정보처리기사 실기 기출문제
- Linux
- APM
- kotlin
- 바이브코딩
- Pinpoint
- MySQL
- Java
- Spring
- CloudWatch
- 오블완
- CKA
- AI
- golang
- CKA 기출문제
- go
- aws
- SRE
- tucker의 go 언어 프로그래밍
- PETERICA
- 코틀린 코루틴의 정석
- minikube
- kotlin querydsl
- 컨텍스트 엔지니어링
- Kubernetes
Archives
- Today
- Total
피터의 개발이야기
[Node.js] Node.js vs Java에서 값 참조와 객체 복사 차이 본문
반응형
ㅁ 들어가며
Node.js에서의 참조 vs 복사, 그리고 불변성(immutability) — 익숙하게 들리는 개념이지만, Java(Spring) 백엔드 개발자로서 실제로 Node.js 게이트웨이 필터를 변경하면서 참조 전달 특성 때문에 고생한 경험이 있다. 특히 accountId 누락 이슈가 터지고 나서야 이 부분을 제대로 이해하게 되었다. 여기선 왜 문제가 생겼는지, 어떻게 해결했는지, 그리고 재발 방지 설계/테스트 원칙을 정리하였다.
ㅁ 문제 배경
- 단위 테스트에선
CustomFilter.refineUser()가 정상적으로accountId를 세팅했다. - 하지만 실제 요청 경로는
CustomFilter.refinedBody()→baseBody()→AbstractFilter.refineUser()로 흘러가는데, 부모 로직이accountId타입을 지원하지 않아서 필드가 싹 사라졌다. - 해결:
baseBody()가 실행된 이후 자식 필터의refineUser()를 다시 적용해서accountId를 복구했다.
ㅁ 기본 개념 차이 (Java ↔ Node.js)
ㅇ Java
- 기본형(primitive): 값 자체가 복사됨 (
int,boolean등) - 참조형(reference): 객체의 주소가 전달됨
- 하지만 실무에선 DTO 매핑/복사, final, String 불변 객체 등 불변 패턴에 익숙하다.
- 컨트롤러에서 request 객체 자체를 직접 건드는 일은 거의 없다.
ㅇ Node.js
- 모든 객체가 참조로 전달됨.
let a = {x:1}; let b = a; b.x = 2; // a.x도 2로 바뀜 - 함수에 req 객체를 넘기면 원본에 바로 영향을 준다.
- 얕은 복사(shallow copy)가 많아서 nested object가 원본을 공유할 수 있다.
- Node.js에선 req, body를 직접 수정하는 일이 흔함. Java보다 side effect 위험도가 높다.
ㅁ Java vs Node.js 코드 예시
Java
public void handleRequest(Request req) {
Request copy = new Request(req); // 새 객체 생성
copy.setUser("accountId");
// 원래 req는 그대로
}
Node.js
function handleRequest(req) {
let copy = req; // 같은 참조
copy.user = "accountId";
// 원래 req.user도 바뀌어버림
}
Node.js에서 깊은 복사(deep clone)를 안 하면, 객체를 공유하는 순간부터 수정을 하게 된다.
ㅁ 핵심 개념 정리 (Node.js 관점)
- 참조 전달: JS 객체/배열은 모두 참조
- 불변성: 입력을 직접 수정하지 않고, 얕은/깊은 복사로 새 객체 만들어 반환
- 얕은 복사:
{ ...obj },Object.assign({}, obj) - 깊은 복사:
structuredClone(obj),_.cloneDeep(obj) - 단,
JSON.parse(JSON.stringify(obj))는 Date, Map 등 손실 가능성 있음.
- 얕은 복사:
- 경계에서의 방어적 복사: Router/Service/Filter 진입에서 복사해서 안전한 작업영역을 만든다.
ㅁ 경험 사례 요약
- 원인: 상위 추상화(
baseBody)에서 부모의refineUser()가 먼저 실행되면서accountId가 증발함 - 해결:
baseBody()실행 후 자식 필터의refineUser()재적용으로accountId복원 - 영향: 기존
aiid,botUserKey엔 영향 없음. 운영 경로에 맞는 통합 테스트 필요성 확인됨
ㅁ 안전한 설계 패턴
- 입력 불변 + 새 객체 반환을 기본 원칙으로
result.user = { ...user, type: 'accountId' } - 공용 헬퍼/추상화는 mutate 금지: 주석/테스트로 안심 보장
- 재적용(override) 패턴: 공통 구조 만든 다음, 도메인 특수 로직은 자식 필터에서 최종 확정
- 불가피한 변경은 함수명/주석/테스트로 “mutate” 명확히 알릴 것
ㅁ 짧은 실습 예 (참조 수정 vs 새 객체 반환)
let req = { user: { id: "123", type: "accountId" } };
function refineUser_mutate(user) { user.type = "userId"; return user; } // 원본 변경
function refineUser_clone(user) { return { ...user, type: "userId" }; } // 새 객체 반환
console.log("=== mutate ===");
let r1 = refineUser_mutate(req.user);
console.log(req.user, r1); // 둘 다 type: "userId"
console.log("=== clone ===");
req = { user: { id: "123", type: "accountId" } };
let r2 = refineUser_clone(req.user);
console.log(req.user, r2); // 원본은 accountId, 결과는 userId
ㅁ 백엔드 개발자 관점 take-away
- Node.js는 참조 전달 특성상, 원본 mutate 위험이 크다. 필터/게이트웨이에선 불변/복사 습관 필수
- 공통 추상화의 기본 처리 순서가 도메인 특수 로직을 덮어쓰기 쉬우므로, 확정은 자식/도메인 레이어에서 꼭 재적용
- "테스트는 통과, Dev에서 실패" — 테스트가 운영 경로를 충분히 모사하지 못할 때 자주 터지는 문제
ㅁ 학습 단계 제안
- 참조 vs 값 복사 차이 실험: 코드 찍어보기
- 얕은 복사 vs 깊은 복사 실습
- req 객체 수정 여부 실험: 미들웨어에서 req.user 바꿔서 후속 핸들러에서 영향 확인
- 불변성 패턴 적용: Redux, FP 방식 Node.js 코드에 도입
ㅁ 마무리
Java에서는 request 객체 직접 수정을 거의 안 해서 이런 묘한 node.js의 side effect 버그로 혼란을 겪었다. 하지만, Node.js에선 참조 전달 때문에 req 수정이 일상인듯 하다. 이번 경험은 그 차이를 명확히 체감한 계기고, 앞으로도 경계지점에서 방어적 복사와 운영 경로 기반 통합 테스트를 습관으로 삼아야겠다.
ㅁ 함께 보면 좋은 사이트
ㅇ [Java] 불변 객체(Immutable Object)란 무엇인가?
반응형
'DevOps > Node.js' 카테고리의 다른 글
| [Node.js] localStorage와 sessionStorage의 차이점 정리 (0) | 2025.06.26 |
|---|---|
| [Node.js] 타임아웃 핸들링 방법 (0) | 2025.04.08 |
| [Node.js] Node.js 서버 사이드,찍먹하기 (3) | 2024.09.27 |
| [Node.js] NodeJS 기본문법 (1) | 2024.09.20 |
| [Node.js] PM2를 사용한 Node.js 관리하기(PM2 사용법 정리) (0) | 2024.09.16 |
Comments
