어떻게 하면 모델링을 잘할 수 있을까?
앞서 그렸던 순차도에서 화살표가 있는 선을 메시지(message)라고 부릅니다.
제가 순차도 그릴 때 사용하는 plantuml에서 메시지와 관련한 내용들을 찾아보았습니다.
이번에는 순차도보다 넓은 범위인 객체 지향 모델링을 맥락으로 해서 메시지의 의미를 제미나이에게 물었습니다.[1]
객체 지향 모델링에서 메시지는 객체 간의 상호 작용을 위한 통신 수단입니다. 마치 사람들이 서로 대화를 하듯, 객체들은 메시지를 주고받으며 특정 작업을 요청하거나 정보를 교환합니다.
사람들의 대화에 빗대어 설명하지만, 인간들의 대화와 다른 점은 엄밀한 형식화를 요구한다는 사실입니다. 제미나이는 메시지의 구성 요소를 셋 가지로 제시합니다.
수신 객체: 메시지를 받는 객체입니다.
메소드 이름: 수신 객체가 수행해야 할 작업을 지정하는 메소드의 이름입니다.
인자 (선택 사항): 메소드 실행에 필요한 추가적인 데이터입니다.
제미나이 설명은 조금 더 신뢰를 주는 위키피디아 내용 중에서 컴퓨터 공학의 Message passing 개념과 굉장히 비슷합니다.
In computer science, message passing is a technique for invoking behavior (i.e., running a program) on a computer. The invoking program sends a message to a process (which may be an actor or object) and relies on that process and its supporting infrastructure to then select and run some appropriate code.
하지만, 코드 작성과 모델링을 일대일 대응으로 바라보는 일은 경계할 필요가 있습니다.[2] 여기서는 그저 순차도에서 화살표로 표현하는 개념에 대한 관련 배경 지식을 찾아보았다고 할 수 있습니다.
이번에는 전혀 다른 이야기로 잠시 화제를 전환해 보겠습니다. 실은 이 글을 쓴 계기가 좀 엉뚱한데요. 요즘 The AI Code Editor로 유명한 cursor 체험을 해 보려고 설치를 했습니다. 그러고 나서 채팅으로 가장 간단하게 go와 react를 함께 사용하는 프로그램을 작성해 달라고 부탁한 후에 실행을 해 보고 코드를 보면서 감탄(?)했죠. 지인들 말 대로 개발 공정에 많은 변화가 있겠구나 싶었습니다. 하지만, 제가 지금은 개발자가 아니기 때문에 이내 관심사를 모델링으로 전환하면서 쓴 글입니다.
먼저 cursor가 만들어 준 웹 프로그램을 실행했더니 제목과 함께 웹 화면에 'Hello from Go!'라는 문구가 보였습니다. 사람이 읽을 수 있는 일종의 메시지죠. 그게 어디서 기원한 것인지 코드로 추적해 보았습니다.
자동 생성된 main.go의 관련 코드[4]의 의도를 간단히 해석하면 웹 서버에서 '/api/hello' 요청을 받았을 때, JSON 형식으로 `{"message": "Hello from Go!"}` 를 응답으로 보내는 것이었습니다. 그렇게 보낸 응답을 처리하는 코드는 react 문법을 사용하는 App.js 였습니다. 웹 페이지 안에서 <p> 태그 안에 JSON 형식으로 받은 message 내용을 출력하게끔 코드가 작성되어 있었습니다.
이렇게 변수 이름으로 여러 차례 메시지라는 표현을 보다가 모델링 개념인 메시지를 떠올린 것입니다.
자, 그럼 이 상황을 어떻게 모델링할 수 있을까요? 바로 시도해 보겠습니다. 제 경험에 따라 직관적으로 하기 위해서 어떤 사전 준비도 하지 않았습니다. 일단 코드를 보고 작동 방식을 추정하는 순차도를 그린 것이죠. 보통 이런 방식을 리버스 엔지니어링(reverse engineering)이라고 부르기도 합니다. 아래는 그 결과를 담은 그림입니다.
이 그림은 얼마나 효용성이 있을까요? 과거에 제가 작성하지 않은 프로그램을 분석할 때 자주 사용하던 방식이죠. 그때 이런 유형의 코드 덩어리에서 추출한 정보를 부를 때, 바로 메커니즘mechanism이라는 말을 배웠습니다.
여기서 제 경험에서 나와 템포 조절을 하죠. UML 스펙specification[5]의 용어를 빌어 잠시 이론적인 개념들을 살펴보겠습니다. UML 초기 스펙에는 Mechanism 정의가 있었던 것으로 기억하는데, 2.5.1 버전에는 없네요. 그런데 객체 지향 방식에서는 작동 방식Mechanism을 객체에게 분담하기 때문에 Collaborations가 Mechanism을 그대로 대체할 수 있습니다.
The primary purpose of Collaborations is to explain how a system of communicating elements collectively accomplish a specific task or set of tasks without necessarily having to incorporate detail that is irrelevant to the explanation. Collaborations are one way that UML may be used to capture design patterns.
마지막 문장을 보면 Collaborations로 디자인 패턴을 표현할 수 있다고 하는데, 디자인 패턴 혹은 설계 패턴은 Mechanism과 표현하는 바가 같다고 할 수 있습니다. 작동 방식이 재사용할 수 있는 패턴이냐 아니냐 차이일 뿐이니까요.
UML 스펙에는 Observer 패턴이 예시로 포함되어 있습니다.
제가 그린 그림에 이름을 붙여 보았습니다. Collaboration 이름은 cursor가 붙여 준 제목입니다. 하지만 이 경우에는 순차도보다는 클래스도나 객체도와 같은 형태가 더 나은 방법이 됩니다. 위에서 인용한 UML 스펙의 예시도 그렇죠. 근데 왜 그럴까요?
메시지는 상호작용의 모델링에 쓰는 요소이기 때문입니다. UML 스펙에서 메시지 정의를 찾아보겠습니다.
A Message defines a particular communication between Lifelines of an Interaction.
상호작용을 하려면 객체 사이에 링크가 있어야 합니다. 이를 관계, 연관 따위로 부릅니다. 그리고, 자바 개발자라면 익숙하게 보셨을 의존Dependency 역시 연관이나 관계를 부르는 다른 이름이라 할 수 있습니다.
사람 사이에서도 서로 소통을 하려면 연락처를 소유해야 하고 흔히 서로 관계가 있다고 말하기도 합니다. 상호작용 자체가 아니라 이를 가능하게 하는 구조를 드러낼 때 보통 클래스도를 씁니다. 이를 반영하여 UML 스펙의 예시와 비슷한 모양으로 저의 Collaboration을 변경해 보겠습니다.
이 코드를 작성하도록 제가 커서에 입력했던 명령(혹은 채팅 내용)을 보겠습니다.
가장 간단한 웹 응용 프로그램을 go로 작성해 주세요.
이 프로그램이 react 앱과 상호 작용 하려면 프로젝트 폴더를 어떻게 구성하는 것이 좋은가요?
cursor가 만들어 준 제목 'React + Go 웹 애플리케이션'은 인공 지능의 탁월함을 보여준 사례입니다. 맥락을 읽어서 제가 바라는 바를 문장으로 표현해 준 것이죠. Collaboration 이름으로 손색이 없습니다.
그러나, App.js와 main.go는 어떨까요? 이는 그저 일반적인 프로그래밍 관행에 따른 이름이라 생각합니다. 마치 은행이나 주민센터에서 서류 작성할 때 보던 '홍길동' 같은 것이죠. 그러니 이름을 바꾸는 편이 좋겠습니다. 어떻게 바꿀 수 있을까요?
자연스럽게 <순차도로 사태를 하나씩 것과 곳과 쪽으로 차려내기> 내용의 복습을 해 볼까요? 이 협력Collaraboration에 참여하는 것들이 셋입니다. 그중 둘의 이름을 손 보고 싶은 것이죠. 둘의 쓸모가 드러나는 이름이 좋겠습니다. App.js는 말 그대로 Frontend라고 할 수 있습니다. 브라우저가 웹으로 접근하는 접점(endpoint) 중에서 Frontend 성격이 강하다고 볼 수 있고, main.go는 데이터를 공급하는 Backend 접점(endpoint)으로 볼 수 있습니다. 그런데 이들의 기반을 제공하는 기술이 각각 react와 go이니 이를 덧붙이고 싶은데, 이럴 때 사용하는 UML 요소가 스테레오 타입입니다. 이를 부착하면 다음과 같이 바뀝니다.
브라우저 기준으로 이쪽과 저쪽에서 기대하는 일(역할)을 표기하면 더 명확해질 듯합니다. 해 볼까요?
초간단이지만, Collaraboration 참여하는 것들과 쪽으로서의 역할이나 책임이 개괄적으로 드러나는 그림을 그릴 수 있습니다. 두 그림을 하나로 합쳐 볼까요?
주석
[1] 사용한 프롬프트와 결과입니다.
[2] 아래 내용을 'UML에 대한 과한 기대와 오용' 사례 소개를 위해 썼다가 사족이 될 수 있어서 주석으로 배치합니다.
저는 한때 UML 활용이 생업과 직결되던 시기도 있었습니다. 대략 5년 정도는 제 일에서 UML이 차지하는 비중이 꽤 컸는데, 당시 UML에 대해서 과한 기대를 걸고 있었던 분들이 많다고 느꼈습니다. 그중에서 특히 두 가지 오해나 오용이 가장 마음에 남습니다.
하나는 오해로 인한 과한 기대인데요. 2004년으로 기억하는데 복잡한 소프트웨어 발주 과정에서 미국회사에 보낸 명세에 대한 해석이 달라 분쟁이 심한 곳에서 해법으로 UML을 채택한 뒤에 만들어진 프로젝트에 참여한 일이 있었습니다. 그 프로젝트의 스폰서는 명문대 제어계측학과 출신으로 자기 일에 대한 프라이드가 엄청 강한 분이었는데, UML 스펙을 읽은 후에 분명 자연어로 할 수 없는 정교한 정의가 가능하다고 믿으셨습니다. 모델링이 추상화에 기인한 문제 풀이 방법이라는 본질을 고려하면 과한 기대는 낭비와 실망을 낳게 만듭니다.[3]
또 다른 오용은 당시 모델링 도구들이 코드를 자동 생성해 주는 점에 과도하게 의존한 개발 방식입니다. 앞서 설명한 메시지의 3요소를 생각해 보세요. 수신 객체, 메소드 이름, 인자. 이들 셋을 영문으로 지정하고, 데이터 타입을 명시하면 소위 말하는 인터페이스 수준이지만, 자바 코드나 C# 코드를 만들어 줄 수 있습니다. 하지만, 그렇게 일대일 대응으로 모델링을 바라보면 모델링이란 일의 강점이 매우 축소되고, 심지어 주객이 전도된 도구의 오용이 되는 경우도 발생합니다.
[3] 물론 글로 쓰는 것보다 조금 더 분명하고, 효과적으로 표현할 수는 있지만 엄밀한 규칙을 표현하기 위해서는 UML은 분명 한계가 있었습니다. 이후에 UML은 인간의 소통을 돕기보다는 자동화나 특정 분야의 정교한 메커니즘 표현을 위한 방향으로 발전되어서 2.0 이후에는 저도 UML 변화를 추적하지 않게 되었습니다.
[4] 다음은 cursor가 설명한 코드 설명입니다:
이 코드를 설명해 드리겠습니다:
1. `http.HandleFunc()`는 특정 URL 경로(`"/api/hello"`)에 대한 핸들러 함수를 등록합니다.
2. `enableCORS()`로 감싸진 핸들러는 CORS(Cross-Origin Resource Sharing) 설정을 적용한 후에 실제 핸들러 로직을 실행합니다.
3. 실제 핸들러 함수는 다음과 같은 작업을 수행합니다:
- `w.Header().Set("Content-Type", "application/json")`: 응답의 컨텐츠 타입을 JSON으로 설정
- `fmt.Fprintf(w, "{\"message\": \"Hello from Go!\"}")`: JSON 형식의 응답을 클라이언트에게 전송
따라서 클라이언트가 `http://localhost:8080/api/hello`로 요청을 보내면:
- CORS 헤더가 추가되고
- JSON 형식으로 `{"message": "Hello from Go!"}` 응답을 받게 됩니다.
이는 주로 프론트엔드(예: React)와 백엔드(Go) 간의 통신을 위한 기본적인 API 엔드포인트를 구현한 것입니다.
[5] UML 현재 표준은 2.5.1 버전입니다.
3. 모델링을 Actor로 시작한다는 의미는 무엇인가?
5. 사고와 인식과 표현의 주체인 임자로 욕망을 바라보기