당신의 LLM들은 실제 OOP를 수행하지 않으며, 구조적입니다.
Source: Dev.to
생성형 AI는 매일 코드를 작성합니다
클래스, 서비스, 모델, 컨트롤러. 겉보기에는 모든 것이 올바른 것처럼 보입니다. 컴파일도 되고, 테스트도 통과하고, “작업을 수행합니다.”
그럼에도 반복되는 문제가 있습니다:
LLM이 생성한 코드는 종종 캡슐화가 부실합니다.
“조금”이 아니라.
구조적으로 캡슐화가 부실합니다.
게터와 세터로 가득 찬 클래스, 거의 행동이 없고, 비즈니스 로직이 여기저기 흩어져 있습니다. 요컨대: 객체‑지향이 아니라 데이터‑지향 코드입니다.
왜 그럴까요?
그리고 더 중요한 질문: AI를 사용할 때 어떻게 하면 더 나은 코드를 만들 수 있을까요?
OOP가 원래 의미했던 것 (그리고 우리가 잊은 것)
- 클래스
- private 속성
- getter / setter
- 인터페이스
하지만 이것은 원래의 비전이 아니다.
Alan Kay(OOP의 아버지 중 한 사람)에게 핵심 아이디어는 클래스가 아니라 메시지였다.
“OOP는 나에게 메시징, 로컬 보존 및 보호와 상태‑프로세스 숨기기, 그리고 모든 것의 극단적인 늦은 바인딩만을 의미한다.”
다시 말해:
- 객체는 소통한다
- 객체는 자신의 상태를 스스로 보관한다
- 객체는 내부 로직을 숨긴다
- 객체는 느슨하게 결합된다
그가 사용한 비유는 생물학적이었다: 내부 장기를 드러내지 않고 상호작용하는 자율 세포와 같다.
LLM이 대신 생성하는 것
Typical example generated by an AI:
public class User {
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
깨끗하고 많은 튜토리얼에서 “베스트 프랙티스”라고 소개됩니다. 하지만 이는 캡슐화가 아닙니다.
왜일까요?
- 내부 상태가 외부에 노출됨
- 내부 타입이 고정됨
- 검증 로직이 없음
- 비즈니스 로직이 외부로 밀려남
결과: 행동 로직이 서비스, 컨트롤러 등에 흩어지거나, 더 나빠서는 어디에든 중복되어 나타납니다. 우리는 이를 빈약한 클래스(anemic class) 라고 부릅니다. 즉, 접근자만 가진 단순한 데이터 주머니에 불과합니다.
게터/세터의 잘못된 보안감
게터와 세터는 캡슐화를 제공하는 듯 보이지만 실제로는:
- 내부 구조를 노출한다
- 강한 결합을 만든다
- 구현 결정을 고정시킨다
필드, 그 타입 또는 로직을 변경하면 곧 광범위한 파손이 발생한다. 객체지향 프로그래밍에서 상태를 노출하는 것은 거의 항상 추상화 누수이다.
객체에 물어볼 더 나은 질문
대신에 이렇게 물어보세요:
if (user.getEmail() == null) {
// logic here
}
다음과 같이 물어보세요:
if (user.canBeContacted()) {
// logic here
}
이미 진전이 있습니다:
- 행동이 지역화되었습니다
- 비즈니스 규칙이 객체 안에 있습니다
- 구현이 진화할 수 있습니다
하지만 우리는 더 나아갈 수 있습니다.
메시지와 이벤트 접근 방식
Alan Kay의 비전에서 객체는 자신이 무엇인지 말하지 않고, 요청받은 것에 응답합니다.
상태를 읽는 대신:
- 의도를 보낸다
- 객체가 결정한다
- 상태는 내부에 남는다
이벤트‑드리븐 또는 메시지‑지향 모델은 바로 이것을 가능하게 합니다:
- 내부 상태 전이
- 강력한 디커플링
- 로직이 한 곳에 집중
이것은 “더 복잡한” 것이 아니라 더 명시적입니다.
Why LLMs struggle so much with real encapsulation
It’s not because AIs are “bad.” It’s structural.
- They learn from existing code – GitHub is filled with CRUDs, DTOs, anemic classes.
- Getters / setters are statistically dominant, so they’re “probable” and therefore generated.
- Business behavior is contextual; LLMs excel at the local, less at global consistency.
- Message‑oriented code is less verbose but more conceptual, making it harder to infer without explicit intention.
The AI doesn’t understand your domain. It extrapolates patterns.
AI를 활용해 OOP 코드를 더 잘 작성하는 방법
해결책은 AI 사용을 중단하는 것이 아니라, AI를 더 잘 안내하는 것입니다.
클래스를 생성할 때, 스스로에게 (그리고 모델에게) 다음 질문을 해 보세요:
- 이 클래스가 무언가를 수행하나요, 아니면 단순히 데이터를 전달하나요?
- 객체에 질문을 하나요, 아니면 상태를 읽나요?
- 동작이 지역화되어 있나요, 아니면 흩어져 있나요?
- 호출자를 깨뜨리지 않고 구현을 변경할 수 있나요?
답이 “아니오”라면, 이는 아마도 진정한 OOP가 아닐 것입니다.
실제 문제는 AI가 아니다
- 우리는 빈약한 OOP를 정상화했습니다
- 우리는 캡슐화를 가시성과 혼동했습니다
- 우리는 행동을 데이터 구조로 대체했습니다
LLM은 단지 우리가 수년간 만들어온 것을 재현할 뿐입니다.
결론
캡슐화는 다음이 아니다:
- 프라이빗 필드
- 퍼블릭 getter
- 수동적인 모델
캡슐화는 다음과 같다:
- 자신의 상태를 책임지는 객체
- 지역화된 비즈니스 규칙
- 직접 접근보다 메시지
- 최소한의 결합도
AI가 도움이 될 수는 있지만, 좋은 모델링을 절대 대체할 수는 없습니다.