한가해지면 이런 컨셉으로 언어 만들어 볼까 생각해봤어요.
HO - A Human-Oriented Programming Language
간결하고 직관적인 문법으로 데이터베이스 스키마와 비즈니스 로직을 정의하는 언어
📋 목차
개요
HO는 기존 프로그래밍 언어의 복잡함에서 벗어나 사람이 읽고 쓰기 편한 코드를 작성하기 위해 설계된 언어입니다.
특징
✨ 직관적이고 명확한 문법
📦 블록 기반 구조 (
[[name]])🔗 ERD 스타일 관계 표현
🎯 타입별 명확한 import 구분
🚀 JavaScript로 변환되어 웹 생태계와 호환
⚡ Go로 작성된 고속 컴파일러
🎯 Bun 런타임 최적화
아키텍처
Transpiler 방식:
HO 코드 (.fn.ho, .schema.ho)
↓
Go 컴파일러 (ho)
↓
JavaScript (.js)
↓
Bun / Node.js / Deno컴파일러: Go로 작성 (빠른 컴파일, 단순한 배포)
타겟: JavaScript (npm 생태계 활용)
런타임: Bun 우선 (Node.js 호환)
배포: 단일 바이너리 실행 파일
확장자
소스 파일:
.schema.ho- 데이터베이스 스키마.fn.ho- 함수/비즈니스 로직.type.ho- 커스텀 타입 정의.data.ho- 데이터 (시드, 백업, 테스트).test.ho- 테스트 코드.config.ho- 설정 파일
컴파일된 파일:
.schema.js- Schema → JavaScript.fn.js- Function → JavaScript.type.js- Type → JavaScript
핵심 원칙
1. 명확성 (Clarity)
옵션이어도 명시적 표기 (
-,[])모호함 없이 정확한 의도 전달
2. 직관성 (Intuitive)
한눈에 구조 파악 가능
import 타입 명확히 구분
3. 간결성 (Concise)
불필요한 키워드 제거
최소한의 문법으로 최대 표현
4. 일관성 (Consistent)
형식 순서 고정
일관된 규칙 적용
파일 구조
project/
├── schemas/
│ ├── users.schema.ho
│ ├── posts.schema.ho
│ └── orders.schema.ho
├── functions/
│ ├── auth.fn.ho
│ ├── posts.fn.ho
│ └── orders.fn.ho
├── types/
│ ├── common.type.ho
│ └── enums.type.ho
├── data/
│ ├── users.seed.data.ho
│ ├── posts.seed.data.ho
│ └── backup.data.ho
├── tests/
│ ├── auth.test.ho
│ └── posts.test.ho
└── routes/
└── (SvelteKit 스타일 폴더 구조)기본 문법
블록과 배열
[[name]] # 블록/정의 (테이블, 함수, 클래스 등)
[items] # 배열/리스트접근 제어
[[functionName]] # public (기본)
[[_helperName]] # private (_ prefix)비동기 처리
# await 사용 시 자동으로 async 함수로 처리
body = {
result = await someAsync()
}
return = result함수 Return
# body와 return 분리
body = {
# 모든 로직
value = calculate()
}
return = value # body 내 변수
# 또는 직접 값
body = {
doSomething()
}
return = "success" # 직접 값Export
# 파일 내 모든 정의는 기본적으로 export
# export 키워드 불필요Schema 파일
타입 정의
[[Type]]
type = serial, text, integer, boolean, timestamp, json
default? = Fn, {type}
constraints[]? = primary, unique, required, index?= 옵션 필드Fn= 함수 (예:now()){type}= 선언된 타입 사용 가능
테이블 정의
[[table_name]]
{key = type default? constraints[]?}
column_name = type default constraints관계 표현
관계 | 표기법 | 설명 |
|---|---|---|
일대일 |
| One-to-One |
일대다 |
| One-to-Many (Has Many) |
다대일 |
| Many-to-One (Belongs To) |
다대다 |
| Many-to-Many |
예제
[[Type]]
type = serial, text, integer, boolean, timestamp, json
default? = Fn, {type}
constraints[]? = primary, unique, required, index
# 일대일 관계
[[users]]-[[profiles.user_id]]
{key = type default? constraints[]?}
id = serial - [primary]
email = text - [unique required]
name = text null []
created_at = timestamp now() []
[[profiles]]
{key = type default? constraints[]?}
id = serial - [primary]
user_id = integer - [required]
bio = text null []
avatar = text null []
created_at = timestamp now() []
# 일대다 관계
[[users]>[[posts.author_id]]
{key = type default? constraints[]?}
id = serial - [primary]
name = text - [required]
[[posts]]
{key = type default? constraints[]?}
id = serial - [primary]
author_id = integer - [required]
title = text - [required]
content = text null []
published = boolean false []
# 다대다 관계 (중간 테이블 자동 생성)
[[posts]<>[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
title = text - [required]
[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
name = text - [unique required]
# Self-referencing
[[comments]>[[comments.parent_id]]
{key = type default? constraints[]?}
id = serial - [primary]
post_id = integer - [required]
author_id = integer - [required]
content = text - [required]
parent_id = integer null []Function 파일
Import 구문
[[import type]]
./path/file.type [TypeName1, TypeName2]
[[import function]]
./path/file.fn [functionName1, functionName2]
[[import schema]]
./path/file.schema [tableName1, tableName2]
[[import constant]]
./path/file [CONSTANT_NAME]
[[import class]]
./path/file [ClassName]
[[import from js]]
./path/legacy [SomeLegacyClass]함수 정의
[[functionName]]
params = (param1:type1, param2:type2)
returns = ReturnType
body = {
# 함수 로직
result = await someOperation()
}
return = result원칙:
body= 과정 (모든 로직 실행)return= 결과 (body 내 변수 또는 직접 값)body에서 선언된 모든 변수는 return에서 접근 가능
return에 직접 값("success", 123 등) 작성 가능
클래스 정의
[[class ClassName]]
properties = {
property1: type1
property2: type2
}
[[ClassName.methodName]]
params = (param1:type1)
returns = ReturnType
body = {
# 메서드 로직
result = doSomething()
}
return = result예제
[[import type]]
./$types [PageServerLoad]
./users.type [User, Profile]
[[import function]]
$lib/remote/posts.remote [getAdminStats]
./auth.fn [validateEmail]
[[import schema]]
./users.schema [users, profiles]
# Public 함수
[[load]]
params = ()
returns = PageServerLoad
body = {
stats = await getAdminStats()
validStats = _validateStats(stats)
}
return = {
stats: validStats,
error: null
}
# Private 함수
[[_validateStats]]
params = (stats:any)
returns = any
body = {
if (!stats) return {}
}
return = stats
[[createUser]]
params = (email:text, name:text)
returns = User
body = {
isValid = validateEmail(email)
if (!isValid) return null
user = await db.insert(users, {email, name})
profile = await db.insert(profiles, {user_id: user.id})
}
return = user
# 클래스 예제
[[class EmailService]]
properties = {
apiKey: text
from: text
}
[[EmailService.send]]
params = (to:text, subject:text, body:text)
returns = boolean
body = {
result = await api.post({
to,
subject,
body,
from: this.from
})
}
return = result.successType 파일
(향후 정의 예정)
# Enum 타입
[[Enum.UserRole]]
values = [admin, user, guest]
# Custom 타입
[[Type.Email]]
base = text
pattern = ^[a-z0-9@.]+$Data 파일
데이터 정의
[[format]]
columns = column1 column2 column3
[[data]]
value1 value2 value3
"value with space" value2 -규칙:
공백으로 구분
공백 포함 시
"따옴표"사용null/빈값은
-
용도
시드 데이터 - 개발용 초기 데이터
백업 - 데이터베이스 백업
테스트 픽스처 - 테스트용 데이터
Document DB - NoSQL 스타일 문서 저장
예제
시드 데이터:
# users.seed.data.ho
[[format]]
columns = id name email role created_at
[[data]]
1 admin admin@ho.dev admin 2025-01-01
2 demo demo@ho.dev user 2025-01-08
3 "Min Ho" minho@ho.dev admin -공백 포함 데이터:
# posts.data.ho
[[format]]
columns = id title content author_id views
[[data]]
1 "Hello World" "This is my first post" 1 100
2 "HO Language" "Introducing HO programming language" 1 250
3 Introduction "Simple post" 2 50빈값 처리:
# profiles.data.ho
[[format]]
columns = id user_id bio avatar website
[[data]]
1 1 "Developer from Korea" - -
2 2 - avatar.jpg "https://example.com"
3 3 "AI enthusiast" - -CLI 명령어
# 시드 데이터 삽입
ho seed users.seed.data.ho
# 데이터베이스 백업
ho backup --output backup.data.ho
# 백업 복구
ho restore backup.data.ho
# 데이터 내보내기
ho export users --format ho --output users.data.ho
# 데이터 가져오기
ho import users.data.ho예제
완전한 프로젝트 구조
blog-app/
├── schemas/
│ ├── users.schema.ho
│ ├── posts.schema.ho
│ └── comments.schema.ho
├── functions/
│ ├── auth.fn.ho
│ └── posts.fn.ho
├── types/
│ └── common.type.ho
└── routes/
├── +page.svelte
└── api/
└── posts/
└── +server.ts (여기서 .fn.ho 사용)users.schema.ho
[[Type]]
type = serial, text, integer, boolean, timestamp
default? = Fn, {type}
constraints[]? = primary, unique, required, index
[[users]]-[[profiles.user_id]]
{key = type default? constraints[]?}
id = serial - [primary]
email = text - [unique required]
name = text - [required]
password = text - [required]
created_at = timestamp now() []
[[profiles]]
{key = type default? constraints[]?}
id = serial - [primary]
user_id = integer - [required]
bio = text null []
avatar = text null []
website = text null []
created_at = timestamp now() []posts.schema.ho
[[Type]]
type = serial, text, integer, boolean, timestamp
default? = Fn, {type}
constraints[]? = primary, unique, required, index
[[users]>[[posts.author_id]]
{key = type default? constraints[]?}
id = serial - [primary]
[[posts]<>[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
author_id = integer - [required]
title = text - [required]
content = text - [required]
published = boolean false []
views = integer 0 []
created_at = timestamp now() []
updated_at = timestamp now() []
[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
name = text - [unique required]
slug = text - [unique required]
[[posts]>[[comments.post_id]]
[[users]>[[comments.author_id]]
[[comments]>[[comments.parent_id]]
{key = type default? constraints[]?}
id = serial - [primary]
post_id = integer - [required]
author_id = integer - [required]
content = text - [required]
parent_id = integer null []
created_at = timestamp now() []auth.fn.ho
[[import type]]
./users.type [User, CreateUserDto]
[[import schema]]
./users.schema [users]
[[import function]]
./utils.fn [hashPassword, validateEmail]
[[createUser]]
params = (email:text, name:text, password:text)
returns = User
body = {
isValid = validateEmail(email)
if (!isValid) throw "Invalid email"
hashedPassword = await hashPassword(password)
user = await db.insert(users, {
email,
name,
password: hashedPassword
})
}
return = user
[[login]]
params = (email:text, password:text)
returns = User
body = {
user = await db.findOne(users, {email})
if (!user) return null
isValid = await _verifyPassword(password, user.password)
if (!isValid) return null
}
return = user
[[_verifyPassword]]
params = (plain:text, hashed:text)
returns = boolean
body = {
result = await bcrypt.compare(plain, hashed)
}
return = resultCLI 명령어
현재 구현된 명령어 (v0.1.0)
# 컴파일 (HO → JavaScript)
ho compile <file.fn.ho>
ho compile <file.schema.ho>
# 컴파일 + 실행
ho run <file.fn.ho>
# 버전 확인
ho version
# 도움말
ho help사용 예제
# 함수 컴파일
ho compile functions/auth.fn.ho
# → functions/auth.fn.js 생성
# 스키마 컴파일
ho compile schemas/users.schema.ho
# → schemas/users.schema.js 생성
# 컴파일 + Bun 실행
ho run functions/hello.fn.ho
# 여러 파일 컴파일
for file in functions/*.fn.ho; do
ho compile "$file"
done계획 중인 명령어
# 프로젝트 초기화
ho init
# 개발 서버 실행 (watch 모드)
ho dev
# 프로덕션 빌드
ho build
# DB 마이그레이션
ho migrate
# 코드 생성 (schema → TypeScript types)
ho generate
# 문법 체크
ho check
# 포맷팅
ho format
# 데이터 관련
ho seed <file> # 시드 데이터 삽입
ho backup --output <file> # DB 백업
ho restore <file> # 백업 복구
ho export <table> # 테이블 데이터 내보내기
ho import <file> # 데이터 가져오기컴파일러 개발
Go 프로젝트 구조
ho-lang/
├── main.go # CLI 진입점
├── compiler/
│ ├── parser.go # HO 파싱
│ ├── generator.go # JS 생성
│ └── types.go # AST 정의
├── examples/
│ ├── hello.fn.ho
│ ├── auth.fn.ho
│ └── users.schema.ho
├── tests/
│ └── compiler_test.go
└── go.mod빌드 & 테스트
# 개발 모드 실행
go run main.go compile examples/hello.fn.ho
# 빌드
go build -o ho main.go
# 테스트
go test ./...
# 크로스 컴파일
GOOS=linux GOARCH=amd64 go build -o ho-linux
GOOS=darwin GOARCH=arm64 go build -o ho-darwin
GOOS=windows GOARCH=amd64 go build -o ho.exe로드맵
Phase 1: 기본 구현 ✅ (완료: 2025-01-08)
Go 기반 컴파일러
Function 파서 (.fn.ho)
JavaScript 코드 생성기
기본 CLI (compile, run, version)
async/await 자동 감지
여러 함수 지원
Schema 파서 (.schema.ho) - 진행 중
Import 구문 지원
Phase 2: 핵심 기능 (진행 중)
Schema 파일 완전 지원
관계 표현 (>, <, <>)
Import 구문 처리
Type 파일 형식 확정
에러 메시지 개선
VS Code 익스텐션 (문법 하이라이팅)
Phase 3: 생태계
DB 마이그레이션 도구
문서 사이트
watch 모드 (ho dev)
단일 바이너리 배포 (Bun compile)
npm 패키지 배포
Phase 4: 고도화
타입 추론
린터 & 포맷터
플러그인 시스템
다른 프레임워크 통합 (Next.js, Remix)
퍼포먼스 최적화
기술 스택
컴파일러
언어: Go 1.23+
장점: 빠른 컴파일, 크로스 컴파일, 단일 바이너리
패키지: 표준 라이브러리 (regexp, strings, os)
런타임
타겟: JavaScript (ES2020+)
우선순위: Bun > Node.js > Deno
이유: Anthropic 인수, 고속 실행, npm 호환
개발 도구
빌드: go build
테스트: go test
배포: GitHub Releases (멀티 플랫폼)
기여하기
HO는 오픈소스 프로젝트입니다. 기여를 환영합니다!
방법
이슈 제출: 버그 리포트, 기능 제안
Pull Request: 코드 개선, 문서 수정
예제 공유: 실제 사용 사례
피드백: 문법, API 개선 의견
개발 가이드
# 저장소 클론
git clone https://github.com/minho/ho-lang.git
cd ho-lang
# 의존성 없음 (표준 라이브러리만 사용)
go mod tidy
# 개발 빌드
go build -o ho main.go
# 테스트
go test ./...
# 예제 실행
./ho compile examples/hello.fn.ho관련 링크
GitHub: https://github.com/id-ego/ho (예정)
문서: https://ho-lang.dev (예정)
Discord: https://discord.gg/ho-lang (예정)
Twitter: @ho_language (예정)
라이선스
MIT (예정)
HO Language - Human-Oriented Programming
Created by Minho
Version: 0.1.0Released: 2025-01-08Compiler: Go 1.25+Runtime: Bun / Node.js
핵심 컨셉은 가독성 최적화 모호함 제거 입니다.