Backend 02
IoC, 컨테이너, Bean: 스프링 핵심 개념 이해하기
IoC, 컨테이너, Bean이 각각 따로 외울 용어처럼 보인다.
근거 · 교안 p13-p18
1장: 객체 관리, 누가 할까? - 제어의 역전(IoC)의 탄생
솔라의 손가락이 노트북 키보드 위에서 멈칫했다. 화면에는 솔라가 방금 작성한 몇 줄의 코드가 선명하게 떠 있었다. 주방장을 만들고, 그 주방장을 레스토랑에 배정하는 간단한 내용이었다. 하지만 솔라의 시선은 코드가 아닌, 모니터 옆에 붙여둔 작은 메모지에 박혀 있었다.
객체 생성과 관리를 개발자가 아닌 프레임워크가 담당한다.
스터디에서 본 스프링의 핵심 원칙이라는 문장이었다. 솔라는 고개를 갸웃거렸다. 방금 new Chef() 라고 직접 주방장을 ‘생성’하고, new Restaurant(chef) 라고 ‘관리’까지 한 사람은 자신이었다. 그런데 프레임워크가 이걸 대신한다고?
“언니, 이것 좀 봐.”
거실에서 책을 읽던 루나가 솔라의 부름에 고개를 들었다. 솔라는 휠체어를 돌려 루나에게 다가갔다.
“이 문장, 이상하지 않아? ‘객체 관리를 개발자가 하지 않는다’니. 그럼 개발자는 뭘 해? 내가 지금 막 객체를 만들었는데, 내 코드는 잘못된 거야? 그리고 왜 굳이 프레임워크가 이걸 대신하는 게 더 좋다는 건지 모르겠어.”
솔라의 목소리에는 마치 자기 일자리를 빼앗긴 사람 같은 억울함마저 섞여 있었다. 루나는 잠시 솔라의 화면과 메모지를 번갈아 보더니, 조용히 솔라의 책상 옆으로 다가왔다.
“네 코드가 잘못된 건 아니야. 그건 ‘제어권’이 너한테 있는 방식이지.”
“제어권?”
“응. 레스토랑을 열기 위해 주방장이 필요할 때, 네가 직접 주방장을 찾아 고용하고, 레스토랑에 배치하는 것과 같아. 모든 과정을 네가 직접 통제하고 있잖아.”
루나는 솔라의 코드 한 줄을 손가락으로 가리켰다.
Chef chef = new Chef();
Restaurant restaurant = new Restaurant(chef);
“여기서 Restaurant 객체는 Chef 객체가 있어야만 움직일 수 있어. ‘의존성’을 가지고 있는 거지. 그리고 지금은 솔라 네가 직접 Chef를 만들어서 Restaurant에 넣어주고 있어. 제어권은 명백히 너한테 있지.”
“당연한 거 아니야? 필요한 건 내가 만들어 써야지.”
솔라가 받아쳤다. 루나는 고개를 끄덕이며 작은 질문을 던졌다.
“만약 그 주방장이 특별한 칼이 없으면 요리를 못 한다면 어떡할까?”
“그럼 칼을 만들어 줘야지.”
솔라는 망설임 없이 키보드에 손을 올릴 뻔했다. Knife knife = new Knife(); 를 먼저 만들고, 그 칼을 주방장에게 주는 코드를 추가해야 할 것이다.
“맞아. 그럼 그 칼이 특정 대장장이가 만든 강철로만 만들 수 있다면?”
“어… 그럼 대장장이부터 만들어야겠네. Blacksmith blacksmith = new Blacksmith(), 그걸로 강철을 만들고, 그 강철로 칼을 만들고, 그 칼을 주방장에게 주고… 아.”
솔라는 말을 멈췄다. 레스토랑 하나를 열기 위해 필요한 것들이 연쇄적으로 떠올랐다. 대장장이, 강철, 칼, 주방장… 의존 관계가 복잡해질수록 개발자가 직접 챙겨야 할 객체의 생성 순서와 조합이 기하급수적으로 늘어났다. 작은 부품 하나만 바뀌어도, 그 부품을 사용하는 모든 곳의 코드를 직접 수정해야 했다. 제어권을 쥐고 있다는 것이 오히려 성가신 책임감으로 다가왔다.
“바로 그 지점이야.”
루나가 말했다.
“이제 다른 방식을 상상해 보자. 네가 레스토랑의 사장이긴 한데, 모든 직원을 직접 채용하는 대신, 아주 유능한 매니저를 고용했다고 생각해 봐. 너는 그 매니저에게 ‘나는 최상급 요리를 하는 레스토랑을 원해’라고 요구사항만 말하는 거야.”
“요구사항만?”
“응. ‘우리 레스토랑엔 주방장이 필요하고, 주방장은 숙련된 대장장이가 만든 칼을 써야 해’ 라는 식의 설계도, 혹은 명세서만 전달하는 거지. 그러면 그 다음은 누가 할까?”
”…매니저가 하겠지. 대장장이를 수소문해서 칼을 주문하고, 그 칼을 쓸 줄 아는 주방장을 채용해서 레스토랑에 배치하고.”
“맞아. 매니저는 네가 준 설계도를 보고 필요한 부품(객체)들을 알아서 만들고, 서로 연결해서 최종적으로 완성된 레스토랑을 너에게 주는 거야. 너는 더 이상 대장장이를 만들지, 칼을 만들지 고민할 필요가 없어. 그저 어떤 레스토랑이 필요한지만 선언하면 돼.”
솔라의 눈이 동그래졌다. 아까의 답답함이 조금씩 풀리는 기분이었다. 개발자의 역할이 사라지는 게 아니었다. 역할이 바뀌는 거였다. 부품을 조립하는 노동자에서, 전체 시스템을 설계하는 설계자로.
“아…! 그럼 아까 그 문장은… 내가 객체 생성을 아예 손 놓는다는 뜻이 아니라, ‘어떻게’ 만들지에 대한 제어권을 프레임워크라는 매니저에게 넘긴다는 뜻이구나. 나는 ‘무엇이’ 필요한지만 알려주고!”
솔라는 자신이 썼던 코드를 다시 봤다. new 키워드를 사용해 모든 걸 직접 통제하던 방식이 갑자기 낡아 보였다.
“바로 그거야. 프로그램의 흐름을 제어하는 주도권이 개발자에게서 프레임워크로 넘어갔지. 통제권이 뒤집혔어. 그래서 이걸 ‘제어의 역전(Inversion of Control, IoC)‘이라고 불러.”
루나가 덧붙였다. 제어의 역전. 솔라는 그 단어를 입안에서 굴려보았다. 처음 메모지에서 봤을 때는 그저 외워야 할 기술 용어 같았지만, 이제는 방금 자신이 겪은 생각의 전환을 정확히 설명하는 이름으로 느껴졌다. 개발자가 객체를 관리하지 않는다는 말은, 개발자의 일이 없어지는 게 아니라 더 중요한 설계에 집중하도록 제어의 주체가 바뀐다는 의미였다.
한결 후련해진 표정으로 솔라가 다시 입을 열었다.
“좋아, 이제 ‘왜’ 제어권을 넘기는 게 좋은지는 알겠어. 훨씬 편하고 유연하니까. 그런데 한 가지 궁금한 게 남았어.”
솔라는 루나를 올려다보았다. 머릿속에 새로운 질문이 명확하게 떠올랐다.
“내가 설계도를 주면 알아서 다 해준다는 그 유능한 ‘매니저’ 말이야. 그건 대체 어디에 있는 거고, 어떻게 내 설계도를 읽고 움직이는 거지?“
2장: 역전된 제어를 담당하는 곳 - IoC 컨테이너
솔라의 질문이 공중에 잠시 머물렀다. “그 유능한 매니저, 대체 어디에 있는 거지?” 루나는 대답 대신, 솔라의 책상 위에 놓인 작은 메모 패드와 펜을 조용히 가져왔다. 사각, 사각. 펜이 종이 위를 스치는 소리가 났다. 루나는 메모지 한가운데에 커다란 네모 상자를 그렸다.
상자 외에는 아무것도 없는 백지였다. 루나는 펜을 내려놓고 솔라를 바라보았다. 솔라의 시선은 잠시 허공의 ‘매니저’를 찾다가, 이내 눈앞의 네모난 그림으로 돌아왔다. 그저 텅 빈 사각형일 뿐이었다. 솔라의 질문은 ‘어디에 있냐’는 것이었는데, 언니는 웬 상자를 그리고 있었다.
“이게 그 매니저야? 그냥… 상자?”
솔라의 목소리에는 실망감이 살짝 묻어났다. 프레임워크가 알아서 다 해준다는 그 대단한 존재가 고작 텅 빈 상자라니. 마치 중요한 부품들을 그냥 한곳에 던져두는 창고 같았다.
“반은 맞고 반은 틀려.”
루나가 말했다.
“단순히 객체들을 보관하는 창고는 아니야. 이 상자는 스스로 생각하고 일하는 ‘관리 시스템’에 더 가까워. 우리가 지난번에 이야기했던 유능한 매니저가 일하는 사무실이자, 매니저 그 자체라고 볼 수 있지.”
루나는 다시 펜을 들었다. 그리고 상자 안쪽에 작은 글씨로 Chef, Knife, Restaurant 같은 단어들을 적기 시작했다. 하지만 이전처럼 단어들을 화살표로 잇지는 않았다. 그냥 상자 안에 흩뿌려 놓을 뿐이었다.
“솔라, 네가 아까 ‘설계도만 넘겨준다’고 했지? 그 설계도를 이 상자가 읽는다고 상상해 봐.”
루나는 상자 바깥, 왼쪽 위에 ‘설계도’라고 작게 썼다.
“프레임워크가 시작되면, 가장 먼저 이 거대한 상자를 만들어. 그리고 우리가 작성한 설계도를 읽기 시작하지. 설계도에 ‘최고급 레스토랑이 필요하다’고 적혀 있으면, 상자는 스스로 판단해. ‘아, 레스토랑을 만들려면 주방장이 필요하겠군. 주방장은 칼이 있어야 하고…’ 이런 식으로 말이야.”
루나의 펜 끝이 상자 안의 단어들을 차례로 가리켰다.
“그래서 이 시스템은 먼저 Knife 객체를 만들고, 그 다음엔 그 칼을 사용하는 Chef 객체를 만들어. 마지막으로 완성된 Chef 객체를 Restaurant 객체에 넣어 최종적으로 완성품을 조립하는 거야. 이 모든 과정이 상자 안에서 자동으로 일어나. 개발자가 new를 써서 직접 순서를 정해주지 않아도.”
솔라는 가만히 그림을 들여다보았다. 처음에는 그저 객체들을 담아두는 보관함처럼 보였던 네모 상자가 다르게 보이기 시작했다. 그것은 수동적인 공간이 아니었다. 설계도를 해석하고, 부품을 만들고, 심지어 조립까지 하는 능동적인 작업장이었다. 개발자는 그저 상자 바깥에서 “레스토랑 하나 내놔!” 하고 요청하기만 하면, 상자가 알아서 완성된 레스토랑 객체를 툭 던져주는 그림이 그려졌다.
“아…! 그럼 제어의 역전이라는 게 그냥 추상적인 원칙이 아니라, 바로 이 상자 안에서 실제로 구현되는 거였구나. 프레임워크가 객체를 ‘관리’한다는 말은, 이 상자가 객체의 생성부터 조립까지 모든 과정을 책임진다는 뜻이었어.”
솔라의 목소리에 깨달음이 실렸다. 개발자가 제어권을 넘긴다는 것은 허공에다 책임을 던져버리는 게 아니었다. 바로 이 상자, 이 시스템에게 명확하게 위임하는 행위였던 것이다.
“맞아. 이렇게 제어의 역전 원칙을 바탕으로, 객체의 생성과 관계 설정, 사용, 그리고 소멸까지 생명주기 전체를 관리하는 이 가상의 공간이자 관리 주체를, 우리는 IoC 컨테이너라고 불러.”
IoC 컨테이너. 솔라는 루나가 그려준 그림 위 여백에 그 단어를 따라 적었다. 어지럽게 흩어져 있던 ‘IoC’, ‘프레임워크’, ‘객체 관리’라는 개념들이 ‘컨테이너’라는 하나의 구체적인 공간 안으로 들어와 질서정연하게 자리를 잡는 기분이었다.
“그럼 이제 진짜 궁금한 게 생겼어.”
솔라가 펜 끝으로 컨테이너 안에 적힌 Chef, Restaurant 같은 단어들을 콕콕 찍으며 말했다.
“이 컨테이너가 관리하는 객체들 말이야. 이것들은 그냥 평범한 ‘객체’라고 부르면 되는 거야? 아니면 이 똑똑한 컨테이너 안에서 관리받는 특별한 녀석들이니까, 뭔가 다른 이름이 있는 건 아닐까?“
3장: 컨테이너 속의 객체, Bean - 관계의 완성
솔라의 시선은 루나가 그려준 메모지 속 ‘IoC 컨테이너’ 상자에 머물러 있었다. 제어권을 넘겨받아 객체의 생성부터 조립까지 책임지는 관리자. 그 안에는 Chef와 Restaurant이 부품처럼 자리 잡고 있었다. 솔라는 펜 끝으로 컨테이너 안의 Chef를 콕 찍었다. 그리고는 상자 바깥의 빈 공간을 한번 더 콕 찍었다.
개발자가 new Chef()로 만든 객체와 컨테이너가 관리하는 Chef 객체. 이 둘은 같은 존재일까, 아니면 컨테이너에 들어가는 순간 특별한 자격을 얻는 걸까? 이 미묘한 차이가 그녀의 다음 질문을 만들고 있었다.
루나는 솔라의 펜이 찍은 두 지점을 보며 미소지었다. “똑같은 질문을 할 줄 알았어. ‘이 안의 객체들은 그냥 객체라고 불러, 아니면 뭔가 특별한 이름이 있어?’ 맞지?” 루나는 솔라의 질문에 바로 답하는 대신, 메모지의 컨테이너 상자 바깥쪽 빈 공간에 작은 원을 하나 그렸다. 그리고 그 안에 MyObject라고 적었다.
“이 원은 솔라 네가 방금 new MyObject() 코드로 직접 만든 객체라고 생각해 봐. 이 객체의 생성과 소멸은 누가 책임지지?”
“나지. 내가 만들었고, 필요 없어지면 내가 참조를 끊어서 처리해야 하니까.”
솔라가 망설임 없이 대답했다. 제어권은 명백히 개발자에게 있었다. 루나는 고개를 끄덕이며, 그 MyObject 원에서 컨테이너 상자 안으로 향하는 화살표를 그렸다.
“좋아. 그럼 이제 네가 컨테이너에게 ‘이 MyObject도 네가 좀 관리해 줘’라고 요청하고, 이 객체를 상자 안으로 집어넣었다고 상상해 봐. 자, 이제 이 객체는 어떻게 될까? 혹시 ‘컨테이너용 특수 객체’ 같은 걸로 변신할까?”
솔라의 눈이 반짝였다. 바로 그거였다. 그녀가 막연하게 상상하던 그림. “응. 뭔가 특별한 능력이 생기거나, 컨테이너에 최적화된 새로운 형태로 바뀌는 거 아니야?” 마치 평범한 시민이 특별한 조직의 요원이 되는 것처럼, 객체의 본질 자체가 바뀔 것이라 예상했다.
“객체 자체는 변하지 않아.”
루나의 대답은 단호했다.
“네가 만든 MyObject 클래스가 갑자기 다른 코드로 바뀌는 게 아니야. 주방장 객체가 컨테이너에 들어갔다고 해서 갑자기 요리법을 잊거나 새로운 팔이 돋아나진 않잖아. 똑같은 주방장이야.”
루나는 펜으로 컨테이너 안으로 들어온 MyObject를 툭툭 쳤다.
“달라지는 건 그 객체의 본질이 아니라, 그 객체를 둘러싼 관계와 책임이야. 상자 바깥에 있을 때, 그 객체는 온전히 네 책임이었어. 하지만 상자 안으로 들어오는 순간, 그 객체는 컨테이너의 관리 목록에 등록돼. 생성, 다른 객체와의 관계 설정, 소멸까지. 그 객체의 인생, 즉 생명주기 전체를 컨테이너가 책임지게 되는 거지. 더 이상 네가 아니라.”
솔라는 숨을 죽이고 설명을 들었다. 변신하는 게 아니었다. 신분이 바뀌는 거였다. 혼자 모든 것을 해결해야 했던 개인 사업자에서, 든든한 시스템의 지원을 받는 팀 소속 구성원이 되는 것처럼.
“이렇게 제어의 역전 원칙에 따라 IoC 컨테이너에 등록되어, 생성부터 소멸까지 그 생명주기 전체를 관리받는 특별한 신분을 얻게 된 객체. 우리는 이걸 Bean(빈) 이라고 불러.”
Bean. 솔라는 나지막이 그 단어를 읊조렸다. 커피 원두를 뜻하는 그 평범한 단어. 특별한 종류의 클래스나 복잡한 데이터 구조를 지칭하는 말이 아니었다. 컨테이너와 맺는 ‘관계’를 나타내는 이름이었다. 컨테이너라는 기계 안에서 갈리고 조합되어 멋진 결과물(애플리케이션)을 만들어내는 기본 재료라는 은유처럼 느껴졌다.
순간, 흩어져 있던 세 개의 점이 하나의 선으로 이어졌다.
솔라는 처음 자신을 혼란스럽게 했던 메모를 떠올렸다. 객체 생성과 관리를 개발자가 아닌 프레임워크가 담당한다. 그 문장 옆에, 솔라는 자신만의 언어로 세 개념의 관계도를 그리기 시작했다.
- 원칙(Why): ‘제어의 역전(IoC)‘이라는 거대한 원칙이 있다. 개발자가 부품 조립까지 다 하지 말고, 설계에 집중하자는 약속.
- 관리자(How/Where): 이 원칙을 실제로 구현하는 ‘IoC 컨테이너’라는 유능한 관리자가 있다.
- 관리 대상(What): 이 컨테이너가 관리하는 부품 하나하나를 ‘Bean’이라고 부른다.
IoC, 컨테이너, Bean. 이들은 각각 외워야 할 용어가 아니었다. ‘왜, 어디서, 무엇을’이라는 질문에 답하는, 하나의 거대한 시스템을 설명하는 유기적인 관계였던 것이다.
솔라는 노트북 화면에 띄워뒀던 자신의 코드를 지웠다. new Chef(), new Restaurant(chef) 같은 직접적인 생성 코드들이 사라진 자리에, 그녀는 새로운 메모를 열고 다른 종류의 설계를 시작했다. 마치 컨테이너에게 보낼 작업 지시서처럼.
[작업 지시서: 신규 레스토랑 오픈]
* 필요 부품(Bean) 목록:
1. Chef (최고급 요리사)
2. Restaurant (프렌치 레스토랑)
* 조립 규칙:
- Restaurant은 반드시 Chef를 필요로 한다.
더 이상 어떻게 만들지를 고민하지 않았다. 무엇이 필요한지, 그리고 그것들이 어떤 관계를 맺어야 하는지만 명시할 뿐이었다. 나머지는 컨테이너의 몫이었다.
“‘객체를 개발자가 관리하지 않는다’는 건… 개발자가 설계를 하고, 컨테이너가 조립을 하는 분업이었구나. 그리고 내가 지금 설계도에 적고 있는 이 부품들이 바로 ‘Bean’인 거고.”
솔라의 손가락이 가볍게 엔터 키를 눌렀다. 텅 빈 코드 편집기 대신, 명확한 역할과 관계가 정의된 설계도가 화면을 채웠다. 제어권을 넘긴다는 것의 진짜 의미를, 솔라는 이제 온전히 자신의 것으로 만들었다.