이 문서는 사이트 담당자와 개발자 모두를 위한 인수인계 자료입니다.
비개발자도 콘텐츠 수정·배포 방법을 이해할 수 있도록 작성되었습니다.
- 사이트 개요
- 로컬 개발 서버 실행
- 관리자 패널 사용법
- 콘텐츠 반영 흐름 (내보내기)
- 이미지 파일 관리
- 배포 절차
- 관리자 비밀번호 변경
- 사이트 구조 — 페이지 목록
- 프로젝트 디렉토리 구조
- 기술 스택
- 자주 묻는 질문 (FAQ)
| 항목 | 내용 |
|---|---|
| 사이트명 | 태일씨앤티 공식 홈페이지 |
| 언어 지원 | 한국어 / 영어 (우측 상단 토글) |
| 관리자 패널 | 사이트주소/#/admin |
| 콘텐츠 저장 방식 | public/content.json 파일 1개로 전체 콘텐츠 관리 |
| 호스팅 유형 | 정적 파일 호스팅 (별도 서버·DB 불필요) |
- Node.js 18 이상
- npm 9 이상
# 1. 저장소 클론 후 의존성 설치
npm install
# 2. 환경변수 파일 생성 (.env 파일이 없으면 관리자 로그인 불가)
# .env 파일이 이미 있다면 이 단계 생략
cp .env.example .env # 또는 아래 7번 항목 참고하여 직접 생성
# 3. 개발 서버 시작 (http://localhost:5173)
npm run dev
# 4. 프로덕션 빌드 (결과물: dist/)
npm run build
# 5. 빌드 결과 로컬 미리보기
npm run previewhttp://localhost:5173/#/admin
@/ → src/ 를 가리킵니다.
예: import SubPageLayout from '@/components/SubPageLayout'
사이트의 텍스트·이미지·항목은 관리자 패널에서 모두 수정할 수 있습니다.
코드를 몰라도 됩니다.
- 브라우저에서
사이트주소/#/admin입력 - 비밀번호 입력 후 로그인
- 비밀번호 변경 방법은 7번 항목 참고
- 5회 연속 오류 입력 시 15분 자동 잠금
| 탭 | 관리할 수 있는 내용 |
|---|---|
| 프로젝트 | 공사수주 현황 목록 — 프로젝트명(한/영), 발주처(한/영), 연도, 이미지, 메인 노출 여부 |
| 뉴스/공지 | 뉴스 및 공지사항 — 제목, 날짜, 태그, 이미지, 노출 여부 |
| 거래처 | 메인 화면 거래처 로고 슬라이드 — 로고 이미지, 이름, 노출 순서 |
| 회사소개 | 인사말, 경영이념, 연혁, 조직도, 업·면허·인증, 오시는길 |
| 유튜브 | YouTube 채널 ID 및 영상 목록 |
| 인재채용 | 복리후생 카드, 채용FAQ, 채용가이드, 직무소개 (한/영) |
| 푸터 정보 | 주소(한/영), 대표번호, FAX, 이메일, 대표자명 |
| HTML/JS | 외부 스크립트 삽입 — 통계 코드, 채팅 위젯 등 |
| 내보내기 | 편집한 전체 내용을 content.json으로 다운로드 |
- 비밀번호는 평문이 아닌 PBKDF2-SHA256 (10만 회 반복 해시) 로 저장됩니다.
- 비밀번호 원문은 코드 어디에도 존재하지 않으며,
.env파일에 해시값만 보관합니다. - 로그인 성공 시 HMAC-SHA256 서명 세션 토큰이 발급되며, 탭을 닫으면 자동 로그아웃됩니다.
- 5회 오류 시 15분 잠금이 브라우저 단에서 적용됩니다.
관리자 패널에서 수정한 내용은 브라우저 메모리에만 임시 저장됩니다.
실제 사이트에 반영하려면 아래 단계를 완료해야 합니다.
① 관리자 패널 접속 (사이트주소/#/admin)
↓
② 원하는 탭에서 내용 수정
↓
③ [내보내기] 탭 클릭 → "content.json 다운로드" 버튼 클릭
↓
④ 호스팅 파일 관리자에서 public/content.json 을 다운로드한 파일로 교체
↓
⑤ 사이트 새로고침으로 반영 확인
주의: 브라우저를 닫거나 새로고침하면 수정 중인 내용이 사라집니다.
반드시 내보내기 → 다운로드 → 호스팅 업로드 순서로 완료하세요.
이미지는 코드가 아닌 파일 직접 업로드 방식으로 관리합니다.
| 폴더 | 용도 |
|---|---|
public/images/logo/ |
로고 파일 (logo.png, logo-dark.png, logo-light.png) |
public/images/hero/ |
메인 화면 슬라이드 이미지 |
public/images/company/ |
회사소개 페이지 이미지 (인사말, 연혁 등) |
public/images/projects/ |
프로젝트 썸네일 이미지 |
public/images/news/ |
뉴스 썸네일 이미지 |
public/images/clients/ |
거래처 로고 이미지 |
public/images/esg/ |
ESG 페이지 영상·이미지 |
public/images/job/ |
채용 관련 이미지 (복리후생 아이콘, 직무 사진) |
- 이미지 파일을 위 폴더 중 적절한 위치에 호스팅 파일 관리자로 업로드
- 관리자 패널 해당 탭에서 이미지 경로 입력
예:/images/projects/새프로젝트.jpg - 내보내기 → 배포
| 용도 | 권장 크기 | 형식 |
|---|---|---|
| 프로젝트 썸네일 | 800 × 500 px | JPG |
| 뉴스 썸네일 | 800 × 500 px | JPG |
| 거래처 로고 | 200 × 80 px (투명 배경) | PNG |
| 복리후생 아이콘 | 64 × 64 px | PNG |
| 메인 슬라이드 | 1920 × 1080 px | JPG |
이 사이트는 정적 파일 호스팅 방식입니다.
서버 재시작 없이 파일 교체만으로 반영됩니다.
1. 관리자 패널에서 수정
2. [내보내기] → content.json 다운로드
3. 호스팅 파일 관리자 접속
4. public/content.json 파일을 다운로드한 파일로 교체
5. 완료 — 별도 빌드 불필요
# 1. 변경사항 수정 후 빌드
npm run build
# 2. dist/ 폴더 전체를 호스팅 루트에 업로드 (기존 파일 덮어쓰기)
# 3. 빌드 시 dist/content.json 이 초기화될 수 있으므로
# 관리자에서 수정한 최신 content.json 을 별도로 덮어쓰기 권장빌드 결과물:
dist/폴더 안의 파일 전체를 호스팅에 올리면 됩니다.
비밀번호는 코드가 아닌 .env 파일에 해시(PBKDF2-SHA256)로 저장됩니다.
변경은 제공된 스크립트를 사용하며, 아래 순서를 따르세요.
① 새 비밀번호로 해시값 생성 (Node.js 필요)
node scripts/gen-admin-hash.mjs 새비밀번호실행하면 아래처럼 출력됩니다:
✅ .env 파일에 아래 값으로 교체하세요:
VITE_ADMIN_HASH=77b79e937e5d...
VITE_ADMIN_SALT=bdc08c5d40d4...
VITE_ADMIN_SECRET=52e5b1f949...
비밀번호는 10자 이상이어야 합니다.
② 프로젝트 루트의 .env 파일을 열고 위 세 줄로 교체
VITE_ADMIN_HASH=출력된_HASH_값
VITE_ADMIN_SALT=출력된_SALT_값
VITE_ADMIN_SECRET=출력된_SECRET_값
VITE_ADMIN_SECRET은 세션 토큰 서명용입니다.
비밀번호만 바꿀 경우 SECRET 은 유지해도 됩니다.
SECRET 을 바꾸면 기존 로그인 세션이 즉시 무효화됩니다.
③ 빌드 후 배포
npm run build
# → dist/ 폴더를 호스팅에 업로드
.env파일 자체는 절대 커밋하거나 공개 저장소에 올리지 마세요.
.env 파일에 접근할 수 있다면 위 절차대로 새 비밀번호로 교체하면 됩니다.
.env 파일도 없다면 개발자에게 연락하여 재발급 받으세요.
| URL | 페이지명 | 관리 방법 |
|---|---|---|
/ |
메인(홈) | 각 섹션별 관리자 탭 |
/company/greeting |
인사말 | 관리자 → 회사소개 |
/company/philosophy |
경영이념 | 관리자 → 회사소개 |
/company/history |
연혁 | 관리자 → 회사소개 |
/company/organization |
조직도 | 관리자 → 회사소개 |
/company/clients |
주요거래처 | 관리자 → 회사소개 |
/company/certification |
인증서 | 관리자 → 회사소개 |
/company/location |
오시는길 | 관리자 → 회사소개 |
/projects/contracts |
공사수주 현황 | 관리자 → 프로젝트 |
/projects/map |
시공 현황 지도 | 관리자 → 프로젝트 (좌표 입력 필요) |
/sustainability |
ESG 경영 | 코드 수정 필요 |
/media/news |
뉴스/공지 | 관리자 → 뉴스/공지 |
/media/youtube |
YouTube | 관리자 → 유튜브 |
/careers/jobs |
직무소개 | 관리자 → 인재채용 |
/careers/hr |
인사제도 | 코드 수정 필요 |
/careers/guide |
채용가이드 | 관리자 → 인재채용 |
/careers/benefits |
복리후생 | 관리자 → 인재채용 |
/careers/faq |
채용FAQ | 관리자 → 인재채용 |
/#/admin |
관리자 패널 | — |
코드 수정 필요페이지는 콘텐츠가 코드에 내장되어 있어
개발자가 직접 수정 후 빌드·배포해야 합니다.
tailcntweb/
│
├── .env ★ 관리자 비밀번호 해시 저장 (절대 커밋 금지)
├── public/
│ ├── content.json ★ 핵심 콘텐츠 파일 — 관리자 패널로 생성·교체
│ ├── favicon.png
│ └── images/ # 모든 이미지 파일
│ ├── logo/
│ ├── hero/
│ ├── company/
│ ├── projects/
│ ├── news/
│ ├── clients/
│ ├── esg/
│ └── job/
│
├── scripts/
│ └── gen-admin-hash.mjs ★ 관리자 비밀번호 해시 생성 스크립트
│
├── src/
│ ├── main.jsx # 앱 진입점
│ ├── App.jsx # 라우팅, 메인 페이지 스냅 스크롤
│ ├── index.css # 전역 스타일
│ │
│ ├── config/
│ │ ├── routes.js # 모든 URL 경로 상수
│ │ └── navTabs.js # 서브페이지 탭 네비게이션 데이터
│ │
│ ├── contexts/
│ │ ├── ContentContext.jsx ★ content.json 로드·전역 제공 (useContent)
│ │ └── LanguageContext.jsx # 한국어/영어 전환 상태 (useLanguage)
│ │
│ ├── i18n/
│ │ └── translations.js # 헤더·공통 UI 번역 문자열
│ │
│ ├── components/ # 메인 페이지 섹션 컴포넌트
│ │ ├── Header.jsx
│ │ ├── Hero.jsx
│ │ ├── Projects.jsx
│ │ ├── Clients.jsx
│ │ ├── ESG.jsx
│ │ ├── Newsroom.jsx
│ │ ├── Footer.jsx
│ │ └── SubPageLayout.jsx # 서브페이지 공통 레이아웃
│ │
│ └── pages/
│ ├── admin/
│ │ ├── AdminPage.jsx ★ 관리자 패널 (로그인·사이드바·탭 라우팅)
│ │ └── tabs/
│ │ ├── shared.jsx # 공통 UI (Field, Toggle, ItemShell...)
│ │ ├── ProjectsTab.jsx
│ │ ├── NewsTab.jsx
│ │ ├── ClientsTab.jsx
│ │ ├── CompanyTab.jsx
│ │ ├── YouTubeTab.jsx
│ │ ├── CareersTab.jsx
│ │ ├── FooterTab.jsx
│ │ ├── SnippetsTab.jsx
│ │ └── ExportTab.jsx
│ │
│ ├── company/
│ ├── projects/
│ ├── sustainability/
│ ├── media/
│ └── careers/
│
├── package.json
├── vite.config.js # Vite 설정 (@/ 별칭, base './')
└── index.html
{
"_version": 2,
"projects": [...],
"news": [...],
"clients": [...],
"youtube": { "title": "", "videos": [...] },
"footer": { "address_ko": "", "tel": "", "fax": "", "email": "", "ceo_ko": "" },
"greeting": { "ceo_names": "", "img": "" },
"philosophy": { ... },
"history": { "items": [...] },
"organization": { "img": "" },
"certification": { "items": [...] },
"location": { "address_ko": "", "address_en": "", "lat": 0, "lng": 0 },
"careers": {
"benefits": [...],
"faq_ko": [...],
"faq_en": [...],
"guide_ko": [...],
"guide_en": [...],
"job_roles": [...]
},
"snippets": [...]
}import { useContent } from '@/contexts/ContentContext'
export default function SomePage() {
const content = useContent()
// content가 null이면 아직 로딩 중
const items = content?.someKey ?? []
}CMS 데이터가 있으면 우선 사용하고, 없으면 컴포넌트 내 하드코딩 기본값으로 fallback하는 패턴을 따릅니다.
| 분류 | 사용 기술 |
|---|---|
| 프레임워크 | React 18 + Vite 5 |
| 라우터 | React Router v6 (HashRouter) |
| 스타일 | Tailwind CSS |
| 애니메이션 | Framer Motion |
| 아이콘 | Lucide React |
| 지도 | react-kakao-maps-sdk (카카오맵) |
| 콘텐츠 관리 | public/content.json (DB·CMS 없는 정적 파일 방식) |
| 관리자 보안 | PBKDF2-SHA256 (10만 회) + HMAC-SHA256 세션 토큰 |
| 빌드 | Vite → dist/ 폴더 출력 |
HashRouter 사용 이유: 정적 호스팅 환경에서 서버 라우팅 설정 없이 클라이언트 라우팅이 가능하도록
/#/경로형태의 URL을 사용합니다.
Q. 관리자 패널에서 수정했는데 사이트에 반영이 안 됩니다.
A. 내보내기 탭에서 content.json을 다운로드한 뒤 호스팅에 업로드해야 반영됩니다.
브라우저를 닫거나 새로고침하면 수정 중인 내용이 초기화됩니다.
Q. 이미지가 깨져서 나옵니다.
A. 관리자에서 입력한 경로가 실제 파일 위치와 일치하는지 확인하세요.
경로는 /images/폴더명/파일명.jpg 형식이어야 합니다.
Q. 한국어로 수정했는데 영문 페이지가 그대로입니다.
A. 한국어/영어가 별도 필드(_ko / _en)로 관리됩니다. 영문 필드도 함께 수정해주세요.
Q. 비밀번호를 잊어버렸습니다.
A. .env 파일에 접근할 수 있다면 7번 항목 절차대로 새 비밀번호를 생성하여 교체하세요.
Q. 비밀번호를 5번 틀렸더니 잠겼습니다.
A. 15분 후 자동으로 해제됩니다. 브라우저의 로컬스토리지를 직접 초기화해도 됩니다.
Q. ESG 페이지나 인사제도 페이지 내용을 바꾸고 싶습니다.
A. 해당 페이지는 콘텐츠가 코드에 직접 작성되어 있습니다.
개발자가 src/pages/sustainability/ESGPage.jsx 또는 src/pages/careers/HRPage.jsx를 수정 후 빌드·배포해야 합니다.
Q. 유튜브 영상 ID는 어떻게 찾나요?
A. YouTube 영상 URL https://www.youtube.com/watch?v=XXXXXX에서 v= 뒤의 XXXXXX 부분이 영상 ID입니다.
Q. 사이트 URL에 #이 붙는 이유는?
A. 별도 서버 없이 정적 파일 호스팅에서 작동하도록 HashRouter를 사용하기 때문입니다. 기능 동작에는 문제가 없습니다.
Q. 새 프로젝트를 추가하면 지도 페이지에도 자동으로 표시되나요?
A. 지도 페이지(/projects/map)는 위도·경도 좌표를 별도로 입력해야 표시됩니다.
관리자 패널 → 프로젝트 탭에서 해당 항목의 좌표값을 함께 입력하세요.
Q. 코드 빌드 후 content.json이 초기화됩니다.
A. npm run build 실행 시 public/content.json이 dist/로 복사됩니다.
빌드 전 관리자에서 내보낸 최신 content.json을 public/에 먼저 덮어쓰거나,
빌드 후 dist/content.json을 별도 최신 파일로 교체하세요.
추가 문의는 개발 담당자에게 연락하세요.