이 문서는 교육용 서버에서 본인 호스팅 공간의 Git 저장소(repo.git)를
HTTPS REST API로 조작하고 커스텀 버전관리 웹 애플리케이션을 개발하는 방법을 설명합니다.
교육생·강사·관리자 로그인 세션을 기반으로 본인 디렉터리만 안전하게 조작할 수 있습니다.
변경 요약 (v2.2)
- 모든 Git 명령에
TZ=Asia/Seoul→ 커밋 시각 한국 시간(+0900) history의dateKST 정규화 +date_raw- 환경설정: 폴더 단위 대상(
targets) · 예외(excludes) commit은 설정 범위만 Git 스테이징 후 커밋rollback시 범위 내 변경이 있을 때만 자동 백업 커밋- 인증: Git API 키 (
tjgit_…) — 포털 「새 API 키 발급」 - 헤더
Authorization: Bearer <key>또는X-Git-Api-Key/ bodyapi_key - 포털 세션은 키 발급·폐기용 (폴백으로 git_api 호출도 가능)
- 저장소: 공식
repo.git(Git 커밋)
엔드포인트 https://ctrlcv.kr/api.php?action=git_api
메서드 POST · Content-Type: application/json
인증 Git API 키 (권장) · 키 형식 tjgit_ + hex
키 발급 포털 로그인 → 마이페이지 → 새 API 키 발급
워크트리 /home/아이디/public_html
저장소 /home/아이디/repo.git
설정 파일 /home/아이디/git_api_settings.json
타임존 Asia/Seoul (KST)
1. 인증 (Git API 키)
- 포털 마이페이지에서 새 API 키 발급 (값 형식:
tjgit_…, 발급 시 1회 표시, 활성 최대 5개). - Git API 호출 시 키를 헤더 또는 JSON 본문에 포함합니다. 키 소유자 본인 저장소만 조작됩니다.
- 키 발급/목록/폐기는 포털 세션이 필요합니다. 액션:
git_api_key_create,git_api_key_list,git_api_key_revoke. - CORS:
https://*.ctrlcv.kr에서 브라우저 호출 가능 (키는 프론트에 하드코딩하지 말고, 가능하면 서버 경유). - 포털 세션이 있으면 키 없이도 호출 가능(폴백). 학생 사이트·스크립트는 키 사용을 권장합니다.
// 권장: Authorization 헤더
Authorization: Bearer tjgit_xxxxxxxx...
// 또는
X-Git-Api-Key: tjgit_xxxxxxxx...
// 또는 JSON body
{
"git_action": "history",
"api_key": "tjgit_xxxxxxxx..."
}
curl -s https://ctrlcv.kr/api.php?action=git_api \
-H "Authorization: Bearer tjgit_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"git_action":"history"}'
3. 환경설정 (폴더 대상 / 예외)
commit은 설정에 맞는 파일만 repo.git에 커밋합니다.
기본값은 targets: ["*"] (public_html 전체, 강제 예외 제외)입니다.
| targets 값 | 의미 |
|---|---|
| * | public_html 전체 (권장 기본) |
| . | 루트 파일만 (하위 폴더 제외) |
| 01, api, … | 해당 폴더(재귀) 또는 파일 |
강제 제외(항상): .env, .env.example, .git
// 설정 조회
{ "git_action": "get_settings" }
// 설정 저장
{
"git_action": "save_settings",
"targets": [".", "01", "api"],
"excludes": ["node_modules", "*.log", "package-lock.json"]
}
// 저장 전 미리보기
{
"git_action": "preview_targets",
"targets": ["01"],
"excludes": ["01/images"]
}
// get_settings 응답 일부
{
"success": true,
"settings": { "targets": ["*"], "excludes": [], "updated_at": "..." },
"folders": [ { "path": "*", "label": "(전체 public_html)" }, ... ],
"forced_excludes": [".env", ".env.example", ".git"],
"preview_count": 42,
"preview_files": ["index.php", "01/about.php", "..."],
"storage": "repo.git"
}
2. 한국 시간(KST) 정책
서버 OS·PHP·Git 환경이 달라도, Git API는 항상 Asia/Seoul을 사용합니다.
| 구간 | 동작 |
|---|---|
| commit / clone | 쉘 환경 TZ=Asia/Seoul로 Git 실행 → Author/Committer 시각 +0900 기록 |
| history / log | date = KST 정규화 문자열 · date_raw = Git 원본 ISO-8601 · 응답 루트 timezone: "Asia/Seoul" |
| commit 응답 | committed_at (예: 2026-07-29 14:30:00), timezone |
| 기본 커밋 메시지 | commit_msg 생략 시 버전 저장 - Y-m-d H:i:s (KST) |
UI에 시간을 표시할 때는 date(이미 KST)를 그대로 쓰면 됩니다.
절대 시각 재계산이 필요하면 date_raw를 파싱하세요.
4. 주요 API 동작 기능 (git_action)
요청 body는 JSON 객체입니다. git_action이 필수이며, 생략 시 기본값은 status입니다.
저장소가 없을 때 주요 동작 호출 시 repo.git이 자동 초기화됩니다.
4.1 init — 저장소 자동/수동 초기화
{
"git_action": "init"
}
repo.git을 만듭니다. 포털 UI의 기본 진입점은 새 API 키 발급이며,
키 발급 시 저장소가 없으면 자동 초기화됩니다. 커밋/히스토리 호출 시에도 없으면 자동 생성됩니다.
4.2 status — 변경 파일 상태 검사
{
"git_action": "status"
}
수정·미추적 파일 목록을 반환합니다.
// 응답 예시
{
"success": true,
"initialized": true,
"short_output": " M index.html\n?? app.js",
"output": "On branch master\n..."
}
4.3 commit — 버전 커밋 저장
{
"git_action": "commit",
"commit_msg": "메인 디자인 및 반응형 UI 작성 완료"
}
- 변경 파일은 repo.git에 Git 커밋으로 저장됩니다.
- 환경설정의
targets/excludes에 해당하는 파일만 스테이징 후 커밋합니다. - user.name / user.email 자동 설정 · 타임스탬프 KST(+0900).
- 변경이 없으면
success: false와 “커밋할 변경사항이 없습니다” 메시지.
// 응답 예시
{
"success": true,
"message": "Git 커밋이 저장되었습니다. (12개 파일)",
"output": "...",
"file_count": 12,
"files": ["index.php", "01/about.php"],
"committed_at": "2026-07-29 14:30:00",
"timezone": "Asia/Seoul",
"storage": "repo.git"
}
4.4 history / log — 커밋 타임라인 조회
{
"git_action": "history"
}
최근 최대 20개 커밋을 배열로 반환합니다. log는 동일 동작입니다.
// 응답 예시
{
"success": true,
"timezone": "Asia/Seoul",
"commits": [
{
"hash": "a1b2c3d",
"author": "test",
"date": "2026-07-29 14:30:00",
"date_raw": "2026-07-29T14:30:00+09:00",
"message": "메인 디자인 및 반응형 UI 작성 완료"
}
],
"raw_output": "a1b2c3d|test|2026-07-29T14:30:00+09:00|메인 디자인..."
}
| 필드 | 설명 |
|---|---|
| hash | 짧은 커밋 해시 |
| author | 작성자 이름 |
| date | KST 정규화 시각 (Y-m-d H:i:s) — UI 표시용 권장 |
| date_raw | Git 원본 ISO-8601 (오프셋 포함) |
| message | 커밋 메시지 |
| timezone | 응답 루트: 항상 Asia/Seoul |
4.5 rollback / checkout — 지정 커밋으로 라이브 복구
{
"git_action": "rollback",
"commit_hash": "a1b2c3d"
}
지정 해시 상태로 public_html을 체크아웃합니다. 버전관리 대상 범위에 변경이 있을 때만 롤백 전 자동 백업 커밋을 생성합니다.
4.6 diff — 변경 내용 비교
{
"git_action": "diff"
}
// 특정 커밋 기준
{
"git_action": "diff",
"commit_hash": "a1b2c3d"
}
commit_hash 생략 시 HEAD 기준 working tree diff를 반환합니다.
4.7 clone / pull — GitHub 원격 연동
// Clone (HTTPS GitHub URL)
{
"git_action": "clone",
"repo_url": "https://github.com/username/repository"
}
// Pull
{
"git_action": "pull"
}
clone은 --separate-git-dir=repo.git 방식으로 워크트리와 저장소를 분리합니다.
GitHub HTTPS URL 형식만 허용됩니다.
5. 공통 응답 규칙
- 성공 시 대체로
"success": true - 실패 시
"success": false와message - 시간 관련 필드는 한국 시간 기준 (표시용
date/committed_at) - 본문 인코딩: UTF-8 JSON
6. JavaScript fetch() 연동 예제
포털 로그인 후 학생 서브도메인 페이지에서도 동일하게 호출할 수 있습니다.
(반드시 credentials: 'include')
const GIT_API = 'https://ctrlcv.kr/api.php?action=git_api';
// 포털에서 발급한 키 (서버 사이드에 두는 것을 권장)
const GIT_API_KEY = 'tjgit_YOUR_KEY';
async function gitApi(git_action, payload = {}) {
const res = await fetch(GIT_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + GIT_API_KEY
},
body: JSON.stringify({ git_action, ...payload })
});
return res.json();
}
// 1) 커밋 타임라인 (KST date 사용)
async function loadGitHistory() {
const data = await gitApi('history');
if (!data.success) {
console.error(data.message);
return;
}
console.log('timezone:', data.timezone); // "Asia/Seoul"
data.commits.forEach(c => {
// c.date → "2026-07-29 14:30:00" (이미 한국 시간)
console.log(`[${c.date}] ${c.hash} ${c.message}`);
});
}
// 2) 버전 커밋
async function saveVersion(msg) {
const data = await gitApi('commit', { commit_msg: msg });
if (data.success) {
console.log('저장 시각(KST):', data.committed_at);
}
}
// 3) 과거 버전으로 복구
async function restoreVersion(hash) {
const data = await gitApi('rollback', { commit_hash: hash });
console.log(data.message);
}
// 4) 화면에 리스트 렌더링 예시
async function renderHistory(listEl) {
const data = await gitApi('history');
if (!data.success) {
listEl.textContent = data.message || '조회 실패';
return;
}
listEl.innerHTML = data.commits.map(c => `
<li>
<strong>${c.message}</strong>
<span>${c.date}</span>
<code>${c.hash}</code>
</li>
`).join('');
}
7. 오류 메시지
| message | 원인 |
|---|---|
| Git API 키가 필요합니다… | 키/세션 없음 · 포털에서 키 발급 후 Bearer 헤더 사용 |
| 유효하지 않은 Git API 키입니다. | 키 오타·폐기됨·다른 계정 키 |
| 로그인이 필요합니다. | 키 발급/목록 API 호출 시 포털 세션 필요 |
| 권한이 없습니다. | 타인 username 지정 (강사·관리자 외) |
| 올바르지 않은 사용자명입니다. | username 형식 불일치 |
| 웹 호스팅 디렉터리를 찾을 수 없습니다. | public_html 없음 |
| 복원할 커밋 해시(Hash)가 필요합니다. | rollback 시 commit_hash 누락 |
| 올바른 GitHub HTTPS 저장소 URL… | clone 시 repo_url 형식 오류 |
| 올바르지 않은 Git 동작입니다. | 지원하지 않는 git_action |
8. 주의사항
- 버전 기록은 공식 Git 저장소
/home/아이디/repo.git에 커밋으로 보관됩니다. - 커밋 범위는
get_settings/save_settings로 조절합니다. - rollback은 working tree를 덮어씁니다. 범위 내 변경이 있으면 자동 백업 커밋 후 복구합니다.
- 시간 표시는
date/committed_at(KST)을 사용하세요. - 학생 사이트·스크립트는
Git API 키로 호출. 키를 공개 HTML/JS에 올리지 마세요. - 키 유출 시 포털에서 즉시 폐기 후 재발급하세요.
api.php?action=git_api