본문 바로가기
Spring & Boot/소?설

[두 개의 룬, 새로운 시대의 서막] 3화

by 한휴르 2025. 9. 8.

이 소설은 gemini-2.5-pro를 사용하여 작성되었습니다.

3화: 컨트롤러, 세상과 마법을 연결하는 문

이안이 단 10분 만에 웹 마법의 기반을 구축한 것은 로드 스프링에게 분명 충격적인 일이었다. 하지만 그는 여전히 회의적이었다. 뼈대를 빨리 세우는 것과, 그 위에 실제로 살과 근육을 붙여 생명을 불어넣는 것은 전혀 다른 차원의 문제라고 생각했다.

"좋다, 뼈대는 세웠다고 치자." 로드 스프링이 입을 열었다. "하지만 외부 세계의 요청, 예를 들어 '치유 포션 한 병을 만들어줘'라는 요청을 어떻게 받아들이고 처리할 생각이지? 전통적인 방식에서는 web.xml에 명시된 디스패처 서블릿이 모든 요청을 받아, @Controller 룬이 새겨진 마법사에게 전달했다. 그리고 그 마법사는 @RequestMapping이라는 주문을 통해 특정 요청에만 응답했지. 너의 그 작은 책은 이 과정을 어떻게 처리하나?"

그것은 마법의 '입구'를 만드는 과정에 대한 질문이었다. 아무리 강력한 마법이라도 외부와 소통할 수 없다면, 그저 자기만족에 불과했다.

이안은 자신있게 대답했다. "스프링 부트의 방식도 근본은 같습니다, 스승님. 다만 훨씬 직관적일 뿐입니다."

이안은 새로운 자바 클래스 파일을 하나 만들고, 그 위에 @RestController 라는 룬을 새겼다.

"@Controller와 거의 비슷하지만, @RestController는 마법의 결과물을 책이나 양피지 같은 '뷰(View)'의 형태로 보여주는 대신, 데이터 그 자체를 순수한 형태로 외부에 돌려주는 데 특화된 룬입니다. 현대 마법사들이 선호하는 방식이죠."

로드 스프링의 시대에는 마법의 결과(Model)를 어떤 모습으로 보여줄지(View) 결정하는 '뷰 리졸버(ViewResolver)'를 설정하고, JSP나 Thymeleaf 같은 템플릿 엔진을 이용해 HTML 문서를 만드는 것이 일반적이었다. 하지만 현대에는 마법 서버(백엔드)는 순수한 데이터(JSON, XML 등)만 제공하고, 그 데이터를 해석해서 사용자에게 보여주는 역할은 클라이언트(웹 브라우저, 모바일 앱 등)가 담당하는 경우가 많아졌다. @RestController는 바로 이런 'API 서버'를 만드는 데 최적화된 룬이었다.

이안은 이어서 클래스 내부에 메소드를 하나 추가했다.

@GetMapping("/potions/{potionName}")
public String getPotionRecipe(@PathVariable String potionName) {
    return potionName + "의 레시피는 다음과 같습니다...";
}

로드 스프링의 눈이 다시 한번 가늘어졌다. @RequestMapping(value = "/potions/{potionName}", method = RequestMethod.GET) 이라고 길게 써야 했던 주문이, @GetMapping("/potions/{potionName}") 이라는 훨씬 간결한 형태로 바뀌어 있었다.

"@GetMapping, @PostMapping... 이 룬들은 특정 요청 방식(HTTP Method)에만 반응하는 특화된 @RequestMapping입니다. 덕분에 주문이 훨씬 짧아지고 의도도 명확해졌습니다." 이안이 설명했다.

"URL 경로에 있는 {potionName} 부분은 @PathVariable 룬을 통해 메소드의 인자로 손쉽게 가져올 수 있습니다. 과거처럼 복잡한 설정 없이, 단지 룬을 새기는 것만으로 URL의 일부를 마법의 재료로 사용할 수 있게 된 것입니다."

이안은 말을 멈추지 않고 또 다른 메소드를 추가했다.

@PostMapping("/potions")
public String createPotion(@RequestBody Potion newPotion) {
    return newPotion.getName() + " 포션 생성 완료!";
}

"만약 외부에서 새로운 포션을 만들어달라는 요청과 함께 포션의 재료, 이름, 효능 등이 담긴 복잡한 데이터 묶음(JSON)을 보낸다면 어떻게 될까요? 과거에는 HttpServletRequest 객체를 직접 다루거나 여러 복잡한 어노테이션을 조합해야 했습니다. 하지만 이제는 @RequestBody 룬 하나면 충분합니다."

@RequestBody. 이 룬은 외부에서 날아온 데이터 덩어리를 보고, 그 구조와 일치하는 마법 객체(여기서는 Potion 클래스)를 자동으로 생성하여 메소드에 전달해주는 놀라운 능력을 지니고 있었다. 마법사는 더 이상 날아온 데이터가 어떤 형식인지, 어떤 값을 가지고 있는지 일일이 분석할 필요가 없었다. 그저 잘 만들어진 Potion 객체를 받아 사용하기만 하면 되었다.

이 모든 과정에서 XML 설정은 단 한 줄도 필요하지 않았다. @SpringBootApplication 룬이 마법 세계를 열고, 그 안에 @RestController 룬이 새겨진 컨트롤러가 외부와 통하는 문을 만들었다. 그리고 @GetMapping@PostMapping 같은 세분화된 룬들이 각기 다른 종류의 손님(요청)을 맞이할 준비를 마쳤다.

이안이 다시 마법을 실행하자, 내장 톰캣 요정은 변함없이 8080번 포트에서 손님을 기다렸다. 이안은 브라우저를 열어 http://localhost:8080/potions/힐링포션 이라고 입력했다. 잠시 후, 화면에는 "힐링포션의 레시피는 다음과 같습니다..." 라는 문장이 나타났다.

이번에는 마법 도구를 이용해 POST 방식으로 http://localhost:8080/potions 주소에 {"name": "마나포션", "ingredient": "마력의 샘물"} 이라는 데이터를 실어 보냈다. 즉시 "마나포션 포션 생성 완료!" 라는 응답이 돌아왔다.

로드 스프링은 여전히 말이 없었다. 하지만 그의 표정은 더 이상 단순한 회의감이 아니었다. 자신이 수십 년간 쌓아온 지식 체계의 근간이 흔들리는 듯한, 더 깊은 고뇌에 빠져 있었다.

물론 아직 비즈니스 로직도, 데이터베이스 연동도 없는 껍데기에 불과했다. 하지만 외부 세계와 소통하는 '문'을 여는 과정이 이토록 간결하고 우아할 수 있다는 사실은, 늙은 아크메이지에게 신선한 충격을 주기에 충분했다. 이안이 들고 있는 저 작은 책은, 단순히 과정을 생략하는 것이 아니라, 마법의 패러다임 자체를 바꾸고 있었다.

댓글