Five's DEFER is Go's defer in PRG syntax. Same safety guarantee, but without if err != nil pollution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
289 lines
7.7 KiB
Markdown
289 lines
7.7 KiB
Markdown
# Five의 가독성 — AI 시대의 진짜 강점
|
|
|
|
## AI가 코딩하는 시대, 왜 가독성이 중요한가
|
|
|
|
AI가 코드를 자동 생성하는 시대입니다.
|
|
하지만 문제가 있습니다:
|
|
|
|
1. **AI가 복잡한 코드를 만들어냄** — 정상 동작하지만 사람이 이해 못 함
|
|
2. **AI 없이 유지보수 불가** — AI 서비스 중단 시 코드가 블랙박스가 됨
|
|
3. **버그 발견이 어려움** — 코드를 읽을 수 없으면 버그도 못 찾음
|
|
4. **팀 협업 불가** — 작성자 외에 누구도 코드를 이해 못 함
|
|
|
|
**Five는 이 문제를 근본적으로 해결합니다.**
|
|
|
|
## 같은 일, 다른 가독성
|
|
|
|
### 고객 목록 조회
|
|
|
|
```prg
|
|
// Five (PRG)
|
|
USE customers NEW
|
|
SET FILTER TO balance > 10000
|
|
GO TOP
|
|
DO WHILE !Eof()
|
|
? name, city, balance
|
|
SKIP
|
|
ENDDO
|
|
```
|
|
|
|
```go
|
|
// Go
|
|
func listCustomers() {
|
|
db, err := sql.Open("sqlite3", "customers.db")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows, err := db.Query(
|
|
"SELECT name, city, balance FROM customers WHERE balance > $1",
|
|
10000,
|
|
)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var name, city string
|
|
var balance float64
|
|
if err := rows.Scan(&name, &city, &balance); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println(name, city, balance)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
```
|
|
|
|
```python
|
|
# Python
|
|
import sqlite3
|
|
|
|
def list_customers():
|
|
conn = sqlite3.connect('customers.db')
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
'SELECT name, city, balance FROM customers WHERE balance > ?',
|
|
(10000,)
|
|
)
|
|
for row in cursor.fetchall():
|
|
print(row[0], row[1], row[2])
|
|
conn.close()
|
|
```
|
|
|
|
```java
|
|
// Java
|
|
public void listCustomers() throws SQLException {
|
|
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:customers.db");
|
|
PreparedStatement stmt = conn.prepareStatement(
|
|
"SELECT name, city, balance FROM customers WHERE balance > ?")) {
|
|
stmt.setDouble(1, 10000);
|
|
try (ResultSet rs = stmt.executeQuery()) {
|
|
while (rs.next()) {
|
|
System.out.println(
|
|
rs.getString("name") + " " +
|
|
rs.getString("city") + " " +
|
|
rs.getDouble("balance")
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
| 언어 | 줄 수 | 에러 처리 | 비개발자 이해 |
|
|
|------|------|----------|:---:|
|
|
| **Five (PRG)** | **8** | 내장 | **✅ 가능** |
|
|
| Python | 12 | 수동 | △ |
|
|
| Go | 27 | 필수 | ❌ |
|
|
| Java | 15 | 필수 | ❌ |
|
|
|
|
## PRG가 읽기 쉬운 이유
|
|
|
|
### 1. 영어 문장처럼 읽힌다
|
|
|
|
```prg
|
|
USE customers NEW // 고객 파일 열어
|
|
GO TOP // 처음으로 가
|
|
DO WHILE !Eof() // 끝까지 반복해
|
|
IF balance > 10000 // 잔액이 만 넘으면
|
|
? name, balance // 이름이랑 잔액 출력해
|
|
ENDIF // 조건 끝
|
|
SKIP // 다음으로
|
|
ENDDO // 반복 끝
|
|
```
|
|
|
|
프로그래밍을 모르는 사람도 흐름이 보입니다.
|
|
`{`, `}`, `:=`, `->`, `func`, `defer` 같은 기호가 없습니다.
|
|
|
|
### 2. 모든 것이 명시적이다
|
|
|
|
```prg
|
|
// Five — 끝이 명확함
|
|
IF condition
|
|
...
|
|
ENDIF // IF가 여기서 끝남
|
|
|
|
FOR i := 1 TO 10
|
|
...
|
|
NEXT // FOR가 여기서 끝남
|
|
|
|
DO WHILE condition
|
|
...
|
|
ENDDO // WHILE이 여기서 끝남
|
|
```
|
|
|
|
```go
|
|
// Go — } 가 뭘 닫는 건지 세어야 함
|
|
if condition {
|
|
for i := 0; i < 10; i++ {
|
|
if another {
|
|
// ...
|
|
} // ← 이 } 는 inner if
|
|
} // ← 이 } 는 for
|
|
} // ← 이 } 는 outer if
|
|
```
|
|
|
|
### 3. 변수명이 길어도 자연스럽다
|
|
|
|
```prg
|
|
LOCAL cCustomerName, nTotalBalance, dLastPurchase, lIsActive
|
|
|
|
cCustomerName := "Charles Kwon"
|
|
nTotalBalance := 15000.50
|
|
dLastPurchase := Date()
|
|
lIsActive := .T.
|
|
```
|
|
|
|
헝가리안 표기법으로 변수 타입이 이름에 보입니다:
|
|
- `c` = Character (문자열)
|
|
- `n` = Numeric (숫자)
|
|
- `d` = Date (날짜)
|
|
- `l` = Logical (논리값)
|
|
- `a` = Array (배열)
|
|
- `o` = Object (객체)
|
|
|
|
AI가 코드를 작성해도, 변수명만 보면 타입을 알 수 있습니다.
|
|
|
|
### 4. DEFER로 안전하면서도 깔끔하다
|
|
|
|
```go
|
|
// Go — defer는 있지만 err 체크가 코드를 오염
|
|
db, err := sql.Open("sqlite3", dsn)
|
|
if err != nil {
|
|
return fmt.Errorf("open failed: %w", err)
|
|
}
|
|
defer db.Close()
|
|
result, err := db.Query("SELECT ...")
|
|
if err != nil {
|
|
return fmt.Errorf("query failed: %w", err)
|
|
}
|
|
defer result.Close()
|
|
```
|
|
|
|
```prg
|
|
// Five — DEFER + 깔끔한 비즈니스 로직
|
|
db := sql.Open("sqlite", dsn)
|
|
DEFER db:Close() // Go의 defer와 동일!
|
|
|
|
result := SqlScan(db, "SELECT ...")
|
|
// 에러는 BEGIN SEQUENCE로 한 곳에서 처리
|
|
// 그런데 코드는 읽기 쉬움
|
|
```
|
|
|
|
Five는 Go의 `defer`를 `DEFER`로 그대로 지원합니다.
|
|
Go의 좋은 점은 가져오되, `if err != nil` 반복은 없앱니다.
|
|
|
|
## AI 시대 시나리오
|
|
|
|
### 시나리오 1: AI 서비스가 중단됨
|
|
|
|
**Go/Python/Java 프로젝트:**
|
|
- 10만 줄 코드 — 구조 파악에 수일
|
|
- 복잡한 타입 시스템, 제네릭, 인터페이스
|
|
- 프레임워크 의존성 파악 필요
|
|
- 신규 개발자가 인수하려면 수주일
|
|
|
|
**Five 프로젝트:**
|
|
- 같은 기능이 3만 줄 — 코드량 자체가 적음
|
|
- `USE`, `SKIP`, `SEEK` — 명령어가 의미 그대로
|
|
- 경험 없는 개발자도 하루 만에 코드 읽기 가능
|
|
|
|
### 시나리오 2: 긴급 버그 수정
|
|
|
|
```prg
|
|
// 버그가 여기 있다고 가정
|
|
USE orders NEW
|
|
SET FILTER TO date >= Date() - 30
|
|
GO TOP
|
|
DO WHILE !Eof()
|
|
IF amount > 0
|
|
nTotal += amount // ← 여기가 문제: += 대신 = 써야 함?
|
|
ENDIF
|
|
SKIP
|
|
ENDDO
|
|
```
|
|
|
|
비개발자(영업팀, 관리자)도 코드를 읽고 "nTotal이 누적되는 것 같다"를 이해합니다.
|
|
Go 코드에서는 이 수준의 이해가 불가능합니다.
|
|
|
|
### 시나리오 3: AI가 만든 코드 검증
|
|
|
|
```prg
|
|
// AI가 생성한 Five 코드 — 사람이 검증 가능
|
|
FUNCTION CalcDiscount(nPrice, nQuantity)
|
|
LOCAL nDiscount
|
|
IF nQuantity >= 100
|
|
nDiscount := nPrice * 0.15 // 100개 이상 15% 할인
|
|
ELSEIF nQuantity >= 50
|
|
nDiscount := nPrice * 0.10 // 50개 이상 10% 할인
|
|
ELSE
|
|
nDiscount := 0 // 할인 없음
|
|
ENDIF
|
|
RETURN nDiscount
|
|
```
|
|
|
|
비즈니스 담당자가 이 코드를 보고 "100개 이상이면 15% 맞아?"라고 확인할 수 있습니다.
|
|
|
|
## Five + Go = 최적의 조합
|
|
|
|
Five가 쉬운 것만으로 충분한 게 아닙니다.
|
|
**쉬운 코드로 강력한 기능**을 쓸 수 있어야 합니다.
|
|
|
|
```prg
|
|
IMPORT "database/sql"
|
|
IMPORT _ "modernc.org/sqlite"
|
|
|
|
PROCEDURE Main()
|
|
LOCAL db, aRows, i
|
|
|
|
// SQL — 쉬운 PRG 문법으로
|
|
db := sql.Open("sqlite", ":memory:")
|
|
DEFER db:Close()
|
|
|
|
db:Exec("CREATE TABLE orders (id INTEGER, amount REAL)")
|
|
db:Exec("INSERT INTO orders VALUES (1, 15000)")
|
|
|
|
// 결과를 Harbour 배열로 받아서 xBase 스타일로 처리
|
|
aRows := SqlScan(db, "SELECT * FROM orders")
|
|
FOR i := 1 TO Len(aRows)
|
|
? aRows[i]["id"], aRows[i]["amount"]
|
|
NEXT
|
|
|
|
RETURN
|
|
```
|
|
|
|
PRG 문법으로 SQL 데이터베이스를 씁니다.
|
|
Go의 `database/sql` 패키지가 뒤에서 동작하지만,
|
|
코드를 읽는 사람은 **Go를 몰라도** 됩니다.
|
|
|
|
## 한 줄 요약
|
|
|
|
> **AI가 코드를 만들어주는 시대에,
|
|
> 사람이 읽을 수 있는 코드가 진짜 자산입니다.
|
|
> Five는 AI가 만들어도 사람이 검증할 수 있는 유일한 시스템 언어입니다.**
|