Java 01

Java의 핵심 가치: 플랫폼 독립성, 객체지향, 견고한 생태계

Java의 장점이 WORA, JVM, 객체지향, Spring, 채용시장처럼 한꺼번에 나와서 실제 학습 시작점이 흐릿하다.

근거 · 교안 p3-p12

Java의 핵심 가치: 플랫폼 독립성, 객체지향, 견고한 생태계 대표 이미지

1장: 루나: JVM과 WORA, ‘어디서든 실행’의 비밀

솔라의 시선은 모니터 한구석에 적어둔 메모에 머물러 있었다. 며칠 전, 개발자 커뮤니티에서 본 문장이었다.

"Java는 한 번 작성하면 어디서든 실행되고, 객체지향과 안정적인 생태계를 갖춘 언어다."

단어 하나하나는 아는 말이었다. 하지만 한 문장으로 엮이니 되려 안갯속에 잠기는 기분이었다. ‘어디서든 실행’, ‘객체지향’, ‘생태계’. 마치 중요한 보물 세 개를 한 상자에 던져 넣고는 “자, 이게 Java의 전부야”라고 말하는 것 같았다. 각 보물이 왜 가치 있는지, 서로 어떻게 연결되는지는 알려주지 않은 채.

솔라는 마우스를 움직여 메모장 앱을 닫았다. 머릿속을 맴도는 건 문장의 첫 구절이었다. ‘한 번 작성하면 어디서든 실행된다.’ ‘Write Once, Run Anywhere’, 줄여서 WORA라고 부른다는 것도 알고 있었다. 하지만 알면 알수록 더 이상했다. 윈도우와 맥은 운영체제가 완전히 다르지 않나? 리눅스는 또 다르고. 그런데 어떻게 똑같은 코드가 아무런 변경 없이 모든 곳에서 움직인다는 걸까? 마법 같은 말이었다. 솔라는 저도 모르게 중얼거렸다.

“그냥… 되는 건가?”

아무 노력 없이, 그냥. 그럴 리 없었다. 컴퓨터는 그렇게 낭만적으로 동작하지 않는다는 것쯤은 알고 있었다.

“뭐가 그냥 돼?”

소파에 앉아 책을 읽던 루나가 고개를 들었다. 솔라는 기다렸다는 듯이 의자를 돌려 루나를 마주 보았다.

“언니, 자바 말이야. ‘한 번 쓰면 어디서든 돌아간다’는 거. 그거 너무 이상하지 않아? 윈도우용 프로그램을 만들면 보통 맥에서는 안 돌아가잖아. 확장자도 다르고. 그런데 자바는 그냥 된다는 게 말이 돼?”

솔라의 목소리에는 ‘나는 이 말을 믿지 않아’라는 의심이 가득했다. 루나는 책갈피를 끼워 넣고 책을 덮었다. 잠시 생각에 잠긴 듯 솔라의 모니터를 바라보더니, 자신의 노트북을 가리켰다.

“이리 와서 한번 볼래? 작은 실험을 해보자.”

루나의 노트북 화면에는 두 개의 창이 나란히 떠 있었다. 하나는 윈도우 가상 머신이었고, 다른 하나는 리눅스 가상 머신이었다. 익숙한 윈도우 바탕화면과 낯선 리눅스 인터페이스가 한 화면에 공존하는 모습이 묘했다.

루나는 가상 머신 창들을 잠시 내려두고, 자신의 메인 운영체제인 macOS에서 간단한 텍스트 편집기를 열었다. 그리고 아주 짧은 자바 코드를 타이핑했다.

class Hello {
    public static void main(String[] args) {
        System.out.println("안녕, 다른 세상!");
    }
}

“자, 이게 우리가 만든 프로그램이야. Hello.java라는 이름으로 저장할게.”

루나는 터미널을 열어 명령어를 입력했다. javac Hello.java. 잠시 후, Hello.java 파일이 있던 폴더에 Hello.class라는 새로운 파일이 생겨났다.

“이게 방금 우리가 쓴 자바 코드를 컴퓨터가 알아들을 수 있는 중간 언어로 번역한 결과물이야. ‘클래스 파일’이라고 부르지.”

솔라는 고개를 끄덕였다. 여기까지는 아는 내용이었다. 문제는 다음이었다.

“이제 이 Hello.class 파일 하나를 가지고 여행을 떠나볼 거야.”

루나는 Hello.class 파일을 먼저 윈도우 가상 머신 바탕화면으로 드래그해서 옮겼다. 그리고 윈도우의 명령 프롬프트를 열어 java Hello라고 입력했다.

안녕, 다른 세상!

솔라의 눈이 동그래졌다. macOS에서 만든 파일이 윈도우에서 실행되었다. 루나는 아무 말 없이 이번에는 똑같은 Hello.class 파일을 리눅스 가상 머신으로 옮겼다. 리눅스의 터미널을 열고, 똑같은 명령어를 입력했다. java Hello.

안녕, 다른 세상!

“어… 진짜 되네.”

솔라는 허탈한 웃음을 터뜨렸다. “정말 그냥 되는 거였어? 그럼 내가 뭘 오해한 거지?”

“오해한 게 아니야. 중요한 걸 놓친 거지.”

루나는 세 개의 운영체제 화면을 나란히 보여주었다. macOS, 윈도우, 리눅스. 그리고 그 중심에는 세 운영체제를 넘나들며 실행된 Hello.class 파일이 있었다.

“솔라, 네가 만약 프랑스인에게 편지를 쓴다고 생각해봐. 그런데 너는 한국어밖에 못 해. 그럼 어떻게 해야 할까?”

“통역사를 구해야지. 한국어를 프랑스어로 바꿔줄 사람.”

“만약 일본인에게도 같은 내용의 편지를 보내고 싶다면?”

“한국어를 일본어로 바꿔줄 또 다른 통역사가 필요하겠지.”

루나가 빙그레 웃었다. “바로 그거야. 여기서 Hello.class 파일은 네가 쓴 ‘한국어 편지’ 같은 거야. 특정 나라 말이 아니라, 내용을 담은 원본이지.”

루나는 윈도우 화면을 클릭했다. “이 윈도우에는 ‘자바라는 한국어 편지를 윈도우라는 나라의 언어로 통역해줄 통역사’가 미리 설치되어 있어. 리눅스에도 마찬가지고, 내 맥에도 마찬가지야. 우리는 그 통역사를 ‘자바 가상 머신’, 즉 JVM(Java Virtual Machine)이라고 불러.”

그제야 안갯속에 있던 그림이 선명해졌다.

“아!”

솔라는 무릎을 쳤다. “그러니까 자바 코드가 직접 윈도우나 리눅스랑 대화하는 게 아니구나! 각 운영체제에 설치된 ‘자바 통역사(JVM)‘랑만 대화하는 거네. 그러니 자바 코드는 한 번만 쓰면 되는 거고. 진짜 일을 하는 건 각 운영체제에 맞춰 만들어진 서로 다른 JVM들이었던 거네.”

“정확해.”

루나는 Hello.class 파일을 다시 한번 클릭했다. “그래서 ‘한 번 작성하면 어디서든 실행된다’는 말은, 사실 ‘JVM이 설치된 곳이라면 어디서든 실행된다’는 뜻이야. 마법이 아니라, 운영체제와 자바 프로그램 사이에 ‘JVM’이라는 똑똑한 중간 다리를 놓은 거지. 이 다리 덕분에 개발자들은 운영체제가 바뀔 때마다 코드를 다시 만들 필요가 없어진 거고. 이게 자바의 첫 번째 핵심 가치, 플랫폼 독립성이야.”

솔라는 고개를 끄덕였다. 이제 WORA는 더 이상 모호한 마케팅 문구가 아니었다. 명확한 작동 원리를 가진 공학적인 해결책이었다. 얽힌 실타래의 첫 번째 매듭이 풀리는 순간이었다. 하지만 그러자 곧바로 다음 매듭이 손에 잡혔다.

솔라는 다시 모니터에 적어두었던 문장을 떠올렸다. ’…객체지향과 안정적인 생태계를 갖춘 언어다.’

“알겠어. JVM 덕분에 어디서든 실행되는 건 정말 강력한 장점이네. 그런데… 왜 ‘객체지향’이라는 말이 바로 뒤따라 나오는 걸까? 그건 코드를 짜는 방식에 대한 이야기잖아. 어디서든 실행되는 거랑, 코드를 ‘객체’ 단위로 짜는 거랑 무슨 상관이 있는 거지? 이것도 뭔가 연결되어 있는 거야?“

2장: 루나: 객체지향, 코드의 안정성을 높이는 설계 원칙

솔라의 질문이 끝나고 잠시 정적이 흘렀다. 루나는 어젯밤의 대화를 끝낸 소파 자리에 그대로 앉아 있었다. 하지만 노트북은 닫혀 있었고, 대신 작은 화이트보드와 마커펜 두 자루를 가져와 솔라의 책상 위에 올려두었다. 어젯밤의 가상 머신 화면들이 있던 자리였다. 하나는 검은색, 하나는 파란색 펜이었다.

루나는 아무 말 없이 검은색 펜을 들고 보드 중앙에 네모를 그리고 ‘게임 캐릭터’라고 썼다. 그리고 그 아래에 작은 글씨로 두 개의 항목을 추가했다. 전사: 칼로 공격한다, 마법사: 마법으로 공격한다. 아주 간단한 게임 기획서처럼 보였다. 솔라가 의자를 당겨 앉자, 루나는 보드를 반으로 나누는 세로줄을 그었다. 왼쪽에는 ‘방법 A’, 오른쪽에는 ‘방법 B’라고 적었다.

“어제 네가 했던 질문, ‘어디서든 실행되는 것’과 ‘객체지향으로 짜는 것’이 어떻게 연결되냐는 거 말이야.”

루나는 ‘방법 A’ 아래에 검은색 펜으로 빠르게 의사 코드를 적어 내려갔다.

// 방법 A: 하나의 함수로 처리하기
function attack(character_type) {
  if (character_type == "전사") {
    print("칼로 공격!");
  } else if (character_type == "마법사") {
    print("파이어볼 발사!");
  }
}

attack("전사");

코드는 한눈에 들어왔다. 캐릭터의 종류를 알려주면, 그에 맞는 공격 메시지를 출력하는 간단한 함수였다. 솔라는 고개를 끄덕였다. 직관적이고 이해하기 쉬웠다.

“그리고 이게 방법 B.”

루나는 이번엔 ‘방법 B’ 아래에 조금 더 복잡해 보이는 구조를 그렸다.

// 방법 B: 각자 알아서 처리하기
class 전사 {
  function attack() { print("칼로 공격!"); }
}
class 마법사 {
  function attack() { print("파이어볼 발사!"); }
}

전사 a = new 전사();
a.attack();

솔라는 두 코드를 번갈아 보았다. 결과는 같을 것이다. 하지만 ‘방법 B’는 왜인지 모르게 일을 더 복잡하게 만드는 것 같았다. 기능을 전사마법사라는 별개의 덩어리로 나누어 놓았다.

“방법 A가 훨씬 간단한 거 아니야? 한 군데에 모여 있으니 뭐가 어떻게 돌아가는지 바로 보이는데. 방법 B는 코드가 흩어져 있잖아.”

솔라의 말에는 ‘굳이 이렇게 복잡하게 할 필요가 있나?’라는 의문이 섞여 있었다. 코드를 ‘객체’라는 단위로 묶는다는 것이, 지금 보기에는 그저 불필요한 포장처럼 느껴졌다.

루나는 솔라의 반응을 예상했다는 듯, 파란색 마커를 집어 들었다. 그리고 화이트보드 상단, ‘게임 캐릭터’라고 적힌 제목 옆에 새로운 요구사항을 추가했다. 궁수: 활로 공격한다.

“좋아. 그럼 이제 두 방법에 각각 ‘궁수’를 추가해 보자. 솔라, 네가 직접 해볼래?”

루나가 검은색 펜을 솔라에게 건넸다. 솔라는 펜을 받아 들고 먼저 ‘방법 A’를 수정하기 시작했다. 어렵지 않았다. 기존의 if-else 문에 else if 하나를 더 추가하면 끝이었다.

// 방법 A: 하나의 함수로 처리하기
function attack(character_type) {
  if (character_type == "전사") {
    print("칼로 공격!");
  } else if (character_type == "마법사") {
    print("파이어볼 발사!");
  } else if (character_type == "궁수") { // <- 추가된 부분
    print("활을 쏜다!");
  }
}

“간단하네.”

솔라는 자신만만하게 말했다. 이제 ‘방법 B’ 차례였다. 솔라는 잠시 고민하다, 기존의 전사마법사 코드 옆에 새로운 덩어리를 추가했다.

// 방법 B: 각자 알아서 처리하기
class 전사 { ... }
class 마법사 { ... }

class 궁수 { // <- 추가된 부분
  function attack() { print("활을 쏜다!"); }
}

두 방법 모두 성공적으로 ‘궁수’를 추가했다. 솔라는 내심 ‘역시 방법 A가 더 편하다’고 생각하며 펜을 내려놓았다. 그러자 루나가 다시 파란색 펜을 들었다. 그리고 이번에는 ‘전사’의 설명 아래에 작은 글씨를 추가했다. 이제 도끼도 사용할 수 있다.

“이번엔 ‘전사’의 공격 방식을 바꿔볼게. 칼뿐만 아니라 도끼도 쓸 수 있도록. 단, 다른 캐릭터 코드에는 절대 영향을 주면 안 돼.”

솔라는 다시 펜을 들었다. ‘방법 B’는 쉬웠다. 전사 클래스 내부만 살짝 고치면 될 일이었다. 마법사궁수 코드는 쳐다볼 필요도 없었다.

// 방법 B
class 전사 {
  function attack(weapon) {
    if (weapon == "칼") { print("칼로 공격!"); }
    if (weapon == "도끼") { print("도끼로 내려친다!"); }
  }
}
...

문제는 ‘방법 A’였다. 솔라는 attack 함수 안의 if (character_type == "전사") 부분을 수정하기 위해 다가갔다. 그런데 코드를 수정하려던 솔라의 손이 순간 멈칫했다. 거대한 if-else 덩어리 전체가 눈에 들어왔다. 지금 내가 ‘전사’ 부분을 고치다가 실수로 ‘마법사’나 ‘궁수’ 부분을 건드리면 어떡하지? 코드가 길어지고 복잡해질수록 그런 실수를 할 확률도 높아질 터였다.

그 순간, 솔라는 깨달았다. ‘방법 A’의 단순함은 착각이었다. 그것은 모든 책임이 한곳에 집중된 ‘위험한 단순함’이었다. 변경 사항이 생길 때마다 이 거대한 함수 전체를 다시 뜯어보고 테스트해야 했다. 반면 ‘방법 B’는 각자의 책임이 명확하게 분리되어 있었다. 전사를 바꾸고 싶으면 전사 코드만 보면 됐다. 마법사궁수는 자기 일이 아니라는 듯 태연하게 그대로 있었다.

“아…”

솔라는 나직이 탄성을 내뱉었다. “방법 A는… 건물을 증축할 때마다 건물 전체의 설계도를 고쳐야 하는 것 같아. 그러다 기둥 하나 잘못 건드리면 전부 무너질 수도 있고. 근데 방법 B는 그냥… 옆에 새로운 건물을 하나 더 짓는 거네. 기존 건물들은 안전하고.”

“맞아.”

루나가 말했다. “객체지향의 핵심은 바로 그거야. ‘책임의 분리’. 각 객체가 자기 자신의 데이터와 행동을 책임지는 거지. 외부에서는 그 내부가 어떻게 돌아가는지 알 필요 없이, 약속된 방식(attack 함수)으로 사용하기만 하면 돼. 이걸 **‘캡슐화’**라고 불러. 덕분에 우리는 코드를 훨씬 안정적으로 변경하고 확장할 수 있게 돼.”

루나는 화이트보드를 가리켰다. “JVM이 운영체제(환경)의 변화로부터 코드를 독립시켜 준다면, 객체지향 설계는 요구사항의 변화로부터 코드를 독립시켜 주는 거야. 둘 다 ‘한 번 잘 만들어두면, 변화에 쉽게 흔들리지 않는다’는 동일한 목표를 향하고 있어. 하나는 기계의 세상에서, 다른 하나는 논리의 세상에서.”

솔라는 이제야 ‘객체지향’이라는 말이 왜 ‘플랫폼 독립성’ 바로 뒤에 나왔는지 이해할 수 있었다. 둘은 별개의 장점이 아니었다. Java라는 언어가 추구하는 ‘안정성’과 ‘견고함’이라는 가치를 서로 다른 차원에서 구현하는, 한 쌍의 도구였던 것이다. 객체지향은 단순히 코드를 예쁘게 묶는 스타일이 아니라, 복잡하고 거대한 시스템이 시간이 지나도 무너지지 않게 만드는 건축 설계 원리였다.

솔라는 고개를 끄덕이며 확신에 찬 목소리로 말했다. “알겠어. JVM이 안정적인 실행을 보장하는 땅이라면, 객체지향은 그 땅 위에 튼튼한 건물을 짓는 설계법인 셈이네. 이 두 가지만 있으면 정말 웬만한 건 다 만들 수 있겠는데?”

그러다 솔라의 얼굴에 다시 새로운 궁금증이 떠올랐다.

“그런데 언니, 그럼 이상하잖아. 이렇게 튼튼한 땅(JVM)이랑 완벽한 설계도(객체지향)가 있는데, 왜 사람들은 ‘스프링(Spring)’ 같은 프레임워크나 ‘생태계’ 이야기를 그렇게 많이 하는 거야? 이미 다 갖춰진 것 같은데, 그 위에 또 다른 거대한 층이 필요한 이유가 뭐지? ‘안정적인 생태계’라는 건 도대체 뭘 더 해주는 건데?“

3장: 루나: Spring 생태계, 백엔드 표준을 만든 협력

솔라의 질문은 허공에 남았지만, 루나는 대답 대신 어제 사용했던 화이트보드를 다시 가져왔다. 어제의 ‘방법 A’와 ‘방법 B’ 코드는 지워져 있었지만, 보드 한쪽에 그려둔 ‘캡슐화된 객체’ 그림—튼튼한 벽으로 둘러싸인 전사마법사—은 그대로 남아있었다. 마치 잘 지어진 독립적인 건물들 같았다.

루나는 말없이 새로운 객체 그림 몇 개를 더 그렸다. ‘칼’, ‘지팡이’, ‘활’. 그리고는 처음 그려둔 전사 객체 옆에 ‘칼’ 그림을 가져다 붙였다. 마법사 옆에는 ‘지팡이’를, 궁수 옆에는 ‘활’을.

“자, 어제 우리가 만든 튼튼한 건물들이야. 각자 자기 역할에 충실하지. 그런데 이 건물들이 실제로 일을 하려면, 그냥 서 있기만 해서는 안 돼. 전사는 칼을 ‘가져야’ 하고, 마법사는 지팡이를 ‘가져야’ 해. 즉, 서로 필요한 존재가 되는 거지.”

루나는 펜으로 전사 객체와 객체를 화살표로 이었다. 그리고 그 화살표 위에 ‘의존 관계’라고 작게 썼다.

“언니, 그건 당연한 거 아니야? 전사 객체를 만들 때, 그 안에서 칼 객체를 새로 만들어서 쓰면 되잖아. new Sword() 하고.”

솔라는 자신 있게 말했다. 객체지향의 기본 원칙을 이제는 안다고 생각했다. 필요한 부품이 있으면, 그 객체 안에서 직접 만들어서 쓰면 그만이었다.

“물론, 그렇게 할 수 있지. 작은 집을 지을 땐 말이야.”

루나는 솔라의 말을 부정하지 않았다. 대신, 보드 위에 객체 그림을 몇 개 더 추가하기 시작했다. ‘방패’, ‘갑옷’, ‘물약’, ‘음식’. 그리고는 전사 객체를 이 모든 것들과 화살표로 연결했다. 화살표들은 금세 거미줄처럼 얽히기 시작했다.

“그런데 우리가 만들 시스템이 작은 집이 아니라 거대한 도시라면 어떨까? 수백, 수천 개의 건물이 서로 필요한 것을 주고받아야 하는 상황. 전사가 이제 ‘강철 검’이 아니라 ‘얼음 마법 검’을 써야 한다면? 그 검을 만들려면 ‘대장장이’ 객체가 필요하고, 대장장이는 ‘마법 부여사’ 객체를 필요로 한다면? ‘전사’ 코드를 열어서 new SteelSword()new IceMagicSword(new Blacksmith(new Enchanter())) 같은 식으로 계속 바꿔줘야 할까?”

루나가 그린 그림은 더 이상 깔끔한 설계도가 아니었다. 어떤 부품 하나를 바꾸기 위해 다른 부품들의 내부까지 줄줄이 알아야 하는, 복잡하게 얽힌 의존성의 지옥도처럼 보였다. 어제 ‘방법 A’에서 느꼈던 ‘위험한 단순함’과는 또 다른 종류의 혼란이었다. 객체 하나하나는 튼튼하게 잘 만들었는데, 그것들을 조립하는 과정이 너무 복잡하고 위태로워 보였다. 솔라의 미간이 좁혀졌다.

“그건… 끔찍한데. 건물 하나 리모델링하려고 도시 전체의 배선도를 뜯어고치는 기분이잖아. 객체지향으로 변경에 강한 코드를 만들었는데, 조립하는 단계에서 모든 게 망가지는 느낌이야.”

“바로 그 지점이야.”

루나는 얽힌 화살표들을 손바닥으로 쓱 가렸다.

“만약에, 우리가 직접 부품을 만들고 조립하는 게 아니라, 어떤 ‘총책임자’에게 위임한다면 어떨까? 우리는 그냥 ‘나는 칼이 필요해’, ‘나는 방패가 필요해’ 라고 선언만 하는 거야. 그러면 그 똑똑한 총책임자가 알아서 창고에서 가장 좋은 칼과 방패를 찾아다가 우리 손에 쥐여주는 거지. 심지어 그 칼을 만들려면 대장장이가 필요하고, 대장장이에게는 철광석이 필요하다는 사실까지 전부 알아서 챙겨주는 거야.”

솔라의 눈이 반짝였다. “그런 게 가능해? 내가 직접 new를 하지 않아도 된다고?”

“응. 그 ‘총책임자’ 역할을 해주는 게 바로 ‘스프링(Spring)’ 같은 프레임워크야. 개발자는 어떤 부품(객체)이 필요한지만 알려주고, 그 부품을 생성하고, 연결하고, 관리하는 제어의 흐름을 프레임워크에게 넘겨버리는 거지. 이걸 ‘제어의 역전(Inversion of Control)‘이라고 불러.”

루나는 보드 한쪽에 거대한 네모 상자를 그리고 ‘스프링 컨테이너’라고 썼다. 그리고 전사, , 방패 같은 객체 그림들을 그 상자 안으로 옮겨 넣었다. 이제 객체들 사이의 얽힌 화살표는 보이지 않았다. 대신 모든 객체가 ‘스프링 컨테이너’와 느슨하게 연결되어 있었다.

“이제 전사는 더 이상 ‘어떤’ 칼을 쓸지 고민하지 않아. 그냥 ‘무기’라는 역할의 부품이 필요하다고만 말하면 돼. 그러면 스프링이 상황에 맞는 무기(강철 검이든, 마법 검이든)를 알아서 주입(Inject)해 주는 거지. 덕분에 우리는 개별 부품들의 기능 구현에만 집중할 수 있게 돼. 복잡한 조립과 관리는 전문가에게 맡겨두고 말이야.”

그제야 솔라는 마지막 퍼즐 조각이 맞춰지는 것을 느꼈다. ‘안정적인 생태계’라는 말의 진짜 의미를 깨달았다. 그것은 단순히 유용한 도구가 많다는 뜻이 아니었다. 객체지향이라는 훌륭한 설계 원칙을 적용해 거대한 시스템을 만들 때 필연적으로 발생하는 ‘조립의 복잡성’이라는 문제를 해결해주는, 거대한 협력 체계였던 것이다.

“아! 알겠다! JVM이 안정적인 땅을 제공하고, 객체지향이 튼튼한 건물을 짓는 설계도라면, 스프링 같은 생태계는 그 수많은 건물에 전기, 수도, 도로망을 자동으로 깔아주고 관리해주는 도시 기반 시설이었구나! 이 기반 시설이 없으면 아무리 건물이 튼튼해도 그냥 외딴 건물들의 집합일 뿐, 유기적으로 작동하는 대도시가 될 수 없는 거였어.”

솔라는 마침내 자신이 처음 가졌던 의문, 모니터 구석에 적어두었던 한 문장이 왜 그 순서로 쓰여 있었는지 온전히 이해하게 되었다. 그것은 단순한 특징의 나열이 아니라, 하나의 문제를 해결하기 위한 체계적인 과정이었다.

솔라는 자신의 의자를 돌려 모니터를 켰다. 그리고 며칠 전부터 자신을 혼란스럽게 했던 메모장 문장을 지우고, 깨끗한 빈 화면에 새로운 그림을 그리기 시작했다.

가장 아래에는 넓고 평평한 토대를 그리고 ‘JVM (플랫폼 독립성): 어디서든 지을 수 있는 안정적인 땅’ 이라고 적었다.

그 위에는 여러 개의 견고한 블록을 쌓아 올렸다. 각 블록에는 ‘객체 (객체지향): 변화에 강한, 독립적인 건물’ 이라고 썼다.

마지막으로, 그 블록들 사이사이를 잇는 정교한 네트워크 망을 그렸다. 그리고 그 망의 중심에 ‘Spring 생태계: 건물들을 연결하고 관리하는 자동화된 도시 인프라’ 라고 명명했다.

그림을 완성한 솔라는 만족스러운 표정으로 루나를 돌아보았다.

“이제 알겠어. Java가 왜 백엔드 개발의 표준이 되었는지. 이 세 가지가 각자 따로 노는 장점이 아니라, 거대하고 복잡한 시스템을 안정적으로 만들기 위해 서로를 떠받치는 기둥들이었던 거야.”

루나는 솔라가 그린 개념도를 보며 조용히 미소 지었다. 더 이상의 설명은 필요 없었다. 솔라는 이제 Java라는 도구를, 흩어진 부품이 아닌 하나의 완성된 청사진으로 이해하고 있었다.