Immutable: 변하지 않는다는 것의 안도감

하수구 배관공·2025년 12월 30일

알쓸잡

목록 보기
5/6
post-thumbnail

오늘은 가볍게 Immutable(불변성)에 대한 글을 써보려고 합니다.

저는 영어가 모국어가 아닙니다. 그래서인지 'Immutable'이라는 단어를 처음 마주했을 때의 그 낯선 느낌이 아직도 생생합니다.

기억을 더듬어보면 2010년대 초반, 바야흐로 Key-Value Store의 춘추전국시대가 열렸던 시절이었습니다. 우후죽순 쏟아지던 수많은 저장소 구현체들의 논문을 뒤적이다가 이 단어를 처음 발견했죠. 정확한 이름은 가물가물하지만, 데이터를 수정하지 않고 '불변'으로 관리한다는 개념은 당시 저에게 꽤 신선한 충격이었습니다.

자, 추억담은 각설하고 본론으로 들어가 봅시다.

낯선 단어와의 첫 만남

처음 그 단어를 마주했을 때, 직관적으로 다가오지 않았습니다. 사전을 찾아보니 "불변의, 변하지 않는"이라는 뜻이더군요. 당시 저의 짧은 프로그래밍 식견으로는 의아했습니다.

변수(Variable)라는 이름부터가 '변할 수 있는 수'인데, 왜 굳이 변하지 않는 성질을 강조하는지 이해하기 어려웠습니다. 특히 데이터를 저장하고 갱신해야 하는 Key-Value Store에서 '불변'이라니, 뭔가 모순처럼 느껴졌죠.

하드웨어에서의 Immutable

학창 시절 컴퓨터 수업에선 CPU와 Memory에 대해 가르쳤습니다. 교과서에는 ROM(Read Only Memory)와 RAM(Random Access Memory)가 있다고 나오더군요.

ROM과 RAM의 지독한 트레이드오프

ROM을 보면서 메모리는 프로그래밍에서 값을 저장할 수 있는 변수와 같은 것인데, 읽을 수만 있고 값을 바꿀 수는 없다니... 왜 저런 불편한 메모리가 있나 싶었습니다.

반대로 RAM은 마음대로 읽고 쓸 수 있어서 이게 훨씬 우수한 것이라 생각했는데, 이건 또 휘발성 메모리라고 합니다. 컴퓨터 전원을 끄면 내용이 싹 지워져 버린다는 거죠.

아, 그래서 뭔가 미리 저장된(변하지 않는) 정보를 컴퓨터 본체가 가지고 있으려면 ROM에다가 기록해둬야 하는구나 싶었습니다.

그때는 Flash Memory나 SRAM, FRAM 같은 것도 흔치 않던(혹은 없던) 시절이었을 겁니다. 그리고 우리가 무언가를 새로 저장하고 유지하려면 Secondary Storage(보조 기억 장치)를 써야 하는데, 'Secondary'라는 말 자체에서 CPU/Memory랑은 뭔가 거리가 있는 위치에 저장한다는 뉘앙스가 풍기죠. 그게 바로 Hard Disk였습니다. 이야기가 너무 많이 샜네요.

제가 말씀드리고 싶은 건, 얼핏 보기에 Immutable은 결국 Read Only와 비슷한 말 같다는 점입니다. 하지만 개발 경험이 쌓이고 나서야 이 둘 사이에 존재하는 결정적인 차이를 깨달았습니다.

Read-Only는 '권한'이고, Immutable은 '보장'이다

현대적인 시스템, 특히 분산 시스템이나 함수형 프로그래밍에서 이 둘을 구분하는 것은 매우 중요합니다. 핵심은 이것입니다.

Read-Only는 "내가" 못 바꾼다는 뜻이고, Immutable은 "아무도" 못 바꾼다는 뜻이다.

이 미묘한 차이를 이해하는 것이 동시성(Concurrency) 문제 해결의 시작점입니다.

1. Read-Only (The "View"):
이것은 일종의 권한(Permission) 문제입니다. "나는 읽기만 가능해, 하지만 누군가 다른 권한을 가진 사람은 이걸 바꿀 수도 있어"라는 상태죠.
예를 들어, 구글 문서(Google Doc)의 '보기 전용(View Only)' 링크를 받았다고 칩시다. 저는 그 문서를 수정할 수 없지만, 문서의 주인은 제가 보고 있는 와중에도 내용을 수정할 수 있습니다. 제 입장에서는 Read-only지만, 데이터 자체는 변하고 있는 것이죠.

2. Immutable (The "Fact"):
이것은 데이터 자체의 속성(Fact)이자 보장(Guarantee)입니다. "나뿐만 아니라, 그 누구도 이 값을 바꿀 수 없다"는 것입니다.
마치 출판된 책(Printed Book)이나 PDF 파일과 같습니다. 일단 인쇄되어 나오면 저자라 할지라도 그 책의 내용을 바꿀 수 없습니다. 내용을 바꾸고 싶다면? 개정판이라는 완전히 새로운 책(새로운 객체)을 찍어내야 합니다.

멀티 스레드 환경에서 "내가 쓰기를 하지 않는다"는 사실만으로는 부족합니다. 내 등 뒤에서 다른 스레드가 값을 바꾸지 않는다는 '완전한 보장'이 있어야만 비로소 우리는 안심할 수 있습니다.

'Const'와 'Immutable'의 혼동

많은 분이(저를 포함해서) 자주 헷갈리는 또 하나의 개념이 바로 const입니다. JavaScript의 const나 Java의 final 같은 키워드는 변수명(Binding)을 재할당하지 못하게 막는 것이지, 객체 내부의 데이터값 자체를 얼리는 것은 아닙니다. (물론 C++ 같은 언어는 const가 값 자체를 불변으로 만들기도 하지만, 포인터와 결합되면 여전히 헷갈리는 지점이 생깁니다.)

const user = { name: "Alice" };

// 이건 안 됩니다 (const가 변수 재할당을 막음)
user = { name: "Bob" }; // Error!

// 하지만 이건 됩니다! (객체 내부 데이터는 변할 수 있음)
user.name = "Eve"; // OK. 데이터가 변했습니다.

개발자들이 'Immutable'을 외치는 이유는 const가 주는 껍데기뿐인 불변성이 아니라, 데이터 구조 그 자체가 절대 변하지 않는다는 단단함이 필요하기 때문입니다.

Key-Value Store에서의 깨달음

처음 그 단어를 봤던 2010년대 초반의 Key-Value Store들로 돌아가 봅니다. 당시 LevelDB나 Cassandra, 그리고 훗날의 RocksDB까지, 세상을 호령하던 저장소들의 핵심에는 LSM-Tree (Log-Structured Merge-tree)라는 아키텍처가 있었습니다.

이들은 디스크에 저장된 데이터를 절대 수정(Update)하지 않습니다. 대신 변경사항을 메모리에 모았다가, 순차적으로 내려쓰며 SSTable(Sorted String Table)이라는 '불변의 파일'로 굳혀버립니다. 나중에 이 불변 파일들을 정리(Compaction)할지언정, 한번 생성된 파일은 절대 내용을 바꾸지 않는다는 이 철학은 시스템에 놀라운 이점을 가져다주었습니다.

  1. Thread Safe (락이 필요 없음):
    객체가 불변이라면 1,000개의 스레드가 동시에 접근해도 안전합니다. 어차피 아무도 값을 바꿀 수 없으니까요. 복잡한 동기화 비용이 '0'이 됩니다.
  2. 안전한 해시(Caching & Hashing):
    Key-Value Store에서 Key가 가변적이라면 재앙이 닥칩니다. 객체의 값이 변하면 해시코드도 변하고, 결국 맵(Map) 안에 저장된 데이터를 영영 못 찾게 될 수도 있습니다. Immutable은 "한번 정해진 해시값은 영원하다"는 것을 보장합니다.
  3. 시간 여행 (Time-Travel Debugging):
    데이터를 '수정'하는 대신 매번 새로운 버전을 '생성'하기 때문에, 과거의 데이터가 그대로 남아있게 됩니다. (Version 1, Version 2...). 덕분에 문제가 생겼을 때 과거의 상태로 되돌아가서 디버깅하기가 훨씬 수월해집니다. 리액트(React)나 카프카(Kafka) 같은 모던 시스템들이 이런 원리를 적극적으로 활용하고 있죠.

결국 '불변'은 단순히 변화를 거부하는 것이 아니라, '변화를 안전하고 예측 가능한 방식으로 관리하는 기술'이었습니다.

녹슨 어린이의 마무리

저는 요즘 '러린이(Rust 어린이)'로 살고 있습니다. Rust라는 언어를 보니, 모든 변수가 기본적으로 Immutable이더군요. 값을 바꾸고 싶다면 mut 키워드를 명시적으로 붙여야만 합니다. 이제 막 배우는 단계지만, 앞서 살펴본 Immutable의 장점들을 생각해보니 왜 언어 차원에서 이런 제약을 두었는지 짐작이 갑니다. 불편함이 아니라, 안전함을 위한 선택이었겠죠. 그럼 이만.

profile
코딩 배우는 하수구 배관공

0개의 댓글