
AI가 작성한 코드를 리뷰하는 기술을 생각해보자【제3회·최종회】 설계·아키텍처의 관점
요약
AI가 생성한 코드의 설계 및 아키텍처 관점 리뷰 방법을 다룹니다. AI가 놓치기 쉬운 프로젝트 문맥과 단일 책임 원칙(SRP), 의존성 관리의 중요성을 설명합니다.
핵심 포인트
- AI는 문맥 파악이 어려워 장기적 관점의 설계에 취약함
- 단일 책임 원칙(SRP)을 통해 함수의 책임을 명확히 분리해야 함
- 구체적인 구현이 아닌 인터페이스에 의존하도록 설계해야 함
- 함수 설명이 여러 개의 동사로 구성된다면 분할을 검토할 것
지난 회까지 「동작·로직·가독성·퍼포먼스(Performance)·보안(Security)」의 관점을 배웠습니다.
최종회는 가장 심도 있는 주제입니다.
설계·아키텍처(Architecture)의 관점:
「지금 동작하는 것」뿐만 아니라 「반년 후·1년 후에도 무리 없이 변경·확장할 수 있는가」
AI가 가장 어려워하는 영역입니다. 왜냐하면 설계의 좋고 나쁨은, 프로젝트의 문맥(Context)·팀의 상황·미래의 방향성을 알지 못하면 판단할 수 없기 때문입니다.
좋은 설계에는 AI가 가지고 있지 않은 「문맥」이 필요합니다.
| 필요한 것 | 구체적인 내용 |
|---|---|
| 📋 프로젝트의 문맥 | 비즈니스 요구사항·비기능 요구사항(속도·가용성 등) |
| ... |
AI는 코드를 「작성하는 것」은 잘하지만,
이러한 문맥을 가지고 있지 않기 때문에
「그 자리에서는 동작하지만, 장래에 곤란해질 설계」를 생성할 때가 있습니다.
책임의 분리 (Separation of Concerns)란?
「각각의 코드가 한 가지 일만을 담당한다」는 사고방식입니다.
요리에 비유하자면 「조리·플레이팅·뒷정리」를 서로 다른 사람이 담당하는 이미지입니다.📖
전문 용어로는 「단일 책임 원칙 (Single Responsibility Principle)」이라고 부릅니다.
이는 소프트웨어 설계의 중요한 지침을 정리한 「SOLID 원칙」의 첫 번째에 해당합니다.
중급자를 목표로 하는 분들은 꼭 한번 찾아보시기 바랍니다!
아래의 기사도 참고로 확인해 주세요.
AI는 「우선 동작하는 것」을 우선시하기 때문에, 하나의 함수·클래스에 여러 개의 책임을 갖게 하는 경우가 있습니다.
| 책임이 혼재된 코드 | 무엇이 문제인가 |
|---|---|
| 데이터 취득·가공·표시를 하나의 함수에서 수행함 | 수정할 때 영향 범위가 큼 |
| ... |
❌ AI가 하기 쉬운 코드 (책임이 혼재됨)
// 사용자 정보를 취득·가공·메일 전송을 하나의 함수에서 수행함
async function processUser(userId: string) {
const user = await db.user.findUnique({ where: { id: userId } });
...
✅ 사람이 리뷰하여 수정한 코드 (책임을 분리)
// 책임①: 데이터 취득만을 담당하는 함수
async function getUser(userId: string) {
return await db.user.findUnique({ where: { id: userId } });
...
체크 포인트:
함수나 클래스의 설명을 「~하고, ~해서, ~한다」와 같이 여러 개의 동사로 설명해야 한다면, 분할을 검토합시다.
하나의 함수는 「~한다」라는 하나의 동사로 설명할 수 있는 것이 이상적입니다.
의존 관계 (Dependency)란?
「A는 B가 없으면 동작하지 않는다」라는 관계를 말합니다.
의존 관계가 복잡해지면, 한 곳을 변경한 것만으로 다른 곳이 망가질 수 있습니다.
AI는 「지금 동작하는」 코드를 작성하기 때문에, 의존 관계를 정리하지 않고 구현할 때가 있습니다.
| 문제 있는 의존 관계 | 리스크 |
|---|---|
| 구체적인 구현에 직접 의존함 | 구현을 바꾸면 전체가 망가짐 |
| ... |
❌ AI가 하기 쉬운 코드 (구체적인 구현에 직접 의존)
// 메일 전송 구현을 직접 사용함
import { sendgrid } from "@sendgrid/mail";
async function notifyUser(email: string, message: string) {
...
✅ 사람이 리뷰하여 수정한 코드 (인터페이스(Interface)에 의존)
// 메일 전송의 「인터페이스(약속)」를 정의
interface EmailSender {
send(to: string, subject: string, body: string): Promise<void>;
...
이렇게 인터페이스(약속)에 의존하게 해두면, 실무에서 큰 메리트가 생깁니다.
본방 환경: SendGridEmailSender를 전달하여 메일을 실제로 전송
로컬·테스트 환경: ConsoleEmailSender (콘솔에 로그를 남기기만 하는 더미)로 한 줄로 교체
Supabase와 같은 외부 서비스도 마찬가지로, 인터페이스를 사이에 두면 「본방은 Supabase·테스트는 모크(Mock, 가짜)」라는 전환이 놀라울 정도로 간단해집니다.
테스트할 때마다 실제 DB에 액세스할 필요가 없으므로 개발 속도도 향상됩니다.
"이 코드의 일부를 다른 구현으로 바꾸고 싶다면, 얼마나 많은 수정이 필요할까?"
이 질문이 의존 관계(dependency) 문제를 발견하는 힌트가 됩니다.
확장성 (extensibility)이란?
"새로운 기능을 추가할 때, 기존 코드를 많이 바꾸지 않아도 된다"는 성질입니다.
확장성이 낮은 코드는 "한 곳을 바꾸면 다른 곳이 망가지는" 경우가 많습니다.
AI는 "현재의 요구사항"에 맞춘 코드를 작성하지만, "미래의 변경"을 고려하지 않는 경우가 많습니다.
| 확장성 문제 | 구체적인 예 | 발생하는 현상 |
|---|---|---|
| 조건 분기가 계속 늘어남 | 결제 방법이 늘어날 때마다 if/else가 증가함 | 코드가 복잡해져 수정이 두려워짐 |
| 하드코딩된 설정 | 소비세율이 0.1로 직접 작성되어 있음 | 세율 변경 시마다 모든 파일을 수정해야 함 |
| 동일한 코드가 여러 곳에 있음 | 유효성 검사(validation)가 곳곳에 복사/붙여넣기 되어 있음 | 수정 누락이 발생하기 쉬움 |
❌ AI가 저지르기 쉬운 코드 (조건 분기가 계속 늘어남)
// 결제 방법이 늘어날 때마다 if/else가 늘어남
async function processPayment(method: string, amount: number) {
if (method === "credit_card") {
...
✅ 사람이 리뷰하여 수정한 코드 (새로운 결제 방법을 추가하기 쉬움)
// 결제 처리의 "약속"을 정의
interface PaymentProcessor {
process(amount: number): Promise<void>;
...
"새로운 〇〇가 추가되었을 때, 몇 군데를 수정해야 하는가?"
이 질문을 통해 확장성 문제를 발견할 수 있습니다.
이상적인 상태는 "새로운 클래스를 하나 추가하는 것만으로 충분한" 상태입니다.📖
이것은 "개방-폐쇄 원칙 (Open-Closed Principle)"이라는 설계의 기본 형태입니다.
"확장에는 열려 있고 (새로운 것을 추가하기 쉽고), 수정에는 닫혀 있다 (기존 코드를 바꾸지 않아도 된다)\
-
구체적인 구현이 아닌 「약속(Interface, 인터페이스)」에 의존하고 있는가
-
순환 의존 (Circular Dependency, A가 B에 의존하고 B가 A에 의존하는 현상)이 발생하지 않았는가
-
외부 서비스에 대한 의존이 적절히 추상화되어 있는가
-
새로운 기능을 추가할 때, 기존 코드를 거의 변경하지 않아도 되는가
-
조건 분기 (Conditional Branch, 조건문)가 계속해서 늘어나는 설계가 아닌가
-
설정값이 하드코딩 (Hard-coding)되어 있지 않은가 (상수나 환경 변수를 사용하고 있는가)
-
동일한 코드가 여러 곳에 복사되어 있지 않은가
-
설계 의사결정을 ADR (Architecture Decision Record)이나 주석에 기록했는가
-
AI에게 역질문을 하여 설계상의 문제를 발견했는가
-
향후 변경이 예상되는 부분에
TODO:주석을 남겼는가
| 회차 | 관점 | 한마디로 요약하면 |
|---|---|---|
| 제1회 | ✅ 동작의 정확성 | 실제로 실행해 보았는가? 극단적인 케이스 (Edge Case)도 테스트했는가? |
| ... |
AI는 코드를 「빠르고 대량으로」 생성할 수 있습니다.
하지만 「좋은 코드」인지 아닌지를 판단하는 것은 바로 당신 자신입니다.
3회에 걸쳐 배운 리뷰의 관점들을 한 번에 모두 수행하지 못해도 괜찮습니다.
우선 제1회의 체크리스트부터 시작해서, 익숙해지면 성능(Performance)·보안(Security) 관점을 더하고, 최종적으로 **설계·아키텍처(Architecture)**까지 볼 수 있게 되는 것.
이러한 축적이 「AI 코드의 심미안」을 길러줍니다 💪
| 단계 | 목표 | 시기目安 |
|---|---|---|
| 🌱 첫걸음 | 동작 확인 및 엣지 케이스(Edge Case)를 반드시 테스트하기 | 오늘부터 |
| ... |
AI를 능숙하게 다루는 엔지니어란,
AI의 출력을 올바르게 평가·수정·개선할 수 있는 사람을 말합니다. 오늘부터 리뷰 습관을 시작해 봅시다 🌟
💬 질문이나 감상이 있다면 댓글창에 편하게 남겨주세요!
👍 도움이 되었다면 '좋아요'와 '저장(Stock)'을 부탁드립니다!
🎓 여기까지 읽어주셔서 정말 감사합니다!
- 【제1회】 동작·로직·가독성의 관점
- 【제2회】 성능·보안의 관점
- 【제3회】 설계·아키텍처의 관점 (이 기사)
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기