신한대학교 경영대학 글로벌무역학과 · 연구실 홈페이지
- Next.js 16 (App Router, Turbopack) / React 19 / TypeScript
- Tailwind CSS v4
- 한국어 · 영어 전환 (클라이언트 Context 기반)
npm install
npm run dev # 개발 서버 http://localhost:3000
npm run build # 정적 사이트를 out/ 에 생성
npm run deploy # GitHub Pages(gh-pages 브랜치)로 배포정적 내보내기(
output: "export") 구성이라npm run start(=next start)는 쓰지 않습니다. 빌드 결과를 미리 보려면npx serve out처럼 정적 서버로 열면 됩니다.
화면에 보이는 글자는 전부 content/ 안에만 있습니다. 컴포넌트를 열 필요 없이 이 파일들만 고치면 됩니다.
| 파일 | 내용 |
|---|---|
content/site.ts |
연구실 이름·주소·이메일, 네비게이션 메뉴, 구성원 하위 페이지, 공통 문구 |
※ 메뉴명과 페이지 제목(label)은 한/영 구분 없이 항상 영어라 그냥 문자열입니다 |
|
content/research.ts |
연구 분야 5개 (홈 화면 카드 + 논문 태그에 함께 사용) |
content/projects.ts |
연구 사업 목록 (Projects 페이지) |
content/publications.ts |
논문 · 국제학술발표 목록 (Publications 페이지) |
content/people.ts |
교수 프로필 |
content/students.json |
학생 명단 (대학원생 · 학부연구생) |
content/alumni.json |
졸업생 명단 |
content/contact.ts |
모집 안내, 지도 임베드 주소 |
모든 텍스트는 { ko: "한국어", en: "English" } 형태(Localized 타입)로 저장합니다.
title: { ko: "무역안보", en: "Trade Security" }화면에서는 useT() 훅이 현재 언어에 맞는 값을 꺼내 줍니다. 문자열 배열도 동일합니다.
const t = useT();
<h2>{t(area.title)}</h2>
{t(area.keywords).map(...)}선택한 언어는 localStorage(ips-lang)에 저장되고, 저장값이 없으면 브라우저 언어를 따릅니다.
- 논문 추가 →
content/publications.ts의journalPublications맨 앞에 추가.areas는content/research.ts의id를 참조하므로 태그가 자동으로 붙습니다. - 메뉴 추가 →
content/site.ts의navigation배열만 수정하면 데스크톱 메뉴와 모바일 드로어에 한 번에 반영됩니다.
두 파일 모두 배열에 객체를 추가하면 카드가 한 장 늘어납니다.
배열이 비어 있으면 안내 문구가 대신 표시됩니다.
(졸업생 문구는 content/people.ts 의 alumniEmpty, 그 밖에는 content/site.ts 의 ui.preparing)
학생 (content/students.json)
| 필드 | 설명 |
|---|---|
name |
이름 |
group |
"graduate"(대학원생) 또는 "undergraduate"(학부연구생). 이 값에 따라 소제목 아래로 묶입니다 |
degree |
학위 — 석사과정 / 박사과정 / 학부연구생 … |
major |
전공 · 소속 |
interests |
관심 키워드 배열 |
photo |
public/images/members/ 안의 파일명. null 이면 이름 첫 글자가 표시됨 |
links |
{ "type": "github", "url": "..." } 배열. 없으면 []. type 은 linkedin · github · homepage · email 중 하나이며 아이콘으로 표시됩니다 (email 은 mailto: 가 자동으로 붙습니다) |
졸업생 (content/alumni.json)
| 필드 | 설명 |
|---|---|
name |
이름 |
degree |
학위 — 석사 / 박사 … |
year |
졸업 · 수료 연도 |
major |
전공 · 소속 학교 |
current |
현 소속 (직장 · 학교) |
links |
학생과 동일 |
name, degree, major, interests, current 는 모두 { "ko": "...", "en": "..." } 형태로 적습니다.
year, photo, links 는 언어와 무관하므로 그냥 문자열입니다.
components/home/HeroMap.tsx — 2D 캔버스만 사용하며 추가 라이브러리가 없습니다.
무역'안보'를 표현하기 위해 세 겹으로 구성되어 있습니다.
- 대륙 실루엣 — 실제 해안선을 채워 그립니다 (
content/world-land.json) - 교역로 — 경기도(연구실 소재지)와 주요 항만을 잇는 점선. 수출은 금색, 수입은 네이비로 흐름 방향이 반대입니다
- 위협과 방어 — 바깥에서 붉은 궤적이 직선으로 날아와 대한민국 테두리에 부딪혀 튕겨 나갑니다. 부딪히는 면의 법선을 기준으로 실제 반사각을 계산하므로 방향마다 튕기는 각도가 다릅니다
위협은 특정 국가나 항만을 지목하지 않도록 실제 지점이 아닌 방위각으로만 정의합니다.
| 무엇을 바꾸려면 | 어디를 |
|---|---|
| 교역 거점 추가·삭제 | content/globe.ts 의 hubs — flow: "in" | "out" 로 방향 지정 |
| 기준점 위치 | content/globe.ts 의 origin |
| 국토 윤곽 (튕기는 기준선) | content/globe.ts 의 koreaOutline |
| 위협 방향·개수 | HeroMap.tsx 의 THREATS — 들어오는 방위각만 지정, 반사각은 자동 계산 |
| 위협 속도 | HeroMap.tsx 의 THREAT_CYCLE |
| 국토 화면상 위치 | HeroMap.tsx 의 FOCUS 와 NARROW 분기 (좁은 화면은 위쪽·오른쪽으로) |
| 색 | 바꿀 필요 없음 — globals.css 팔레트를 읽어 씁니다 (--color-alert-500 이 위협색) |
| 해안선 정밀도 | scripts/build-world-land.mjs 의 MIN_AREA 후 npm run world:land |
해안선(링 78개)은 world-atlas 지도 데이터에서 미리 뽑아 content/world-land.json(42KB)에 저장해 둡니다.
생성 스크립트만 devDependency 를 쓰고, 실행 시점에는 이 JSON 만 필요합니다.
prefers-reduced-motion이면 움직임을 멈추고, 화면 밖으로 스크롤되면 렌더링을 쉽니다.
색상 · 폰트 · 컨테이너 폭 등 모든 디자인 토큰은 app/globals.css 의 @theme 블록 한 곳에만 있습니다.
컴포넌트에서는 하드코딩된 hex 대신 text-navy-800, bg-surface, text-muted, border-line 같은 유틸리티를 사용합니다.
- 본문 서체: Pretendard (
app/fonts/PretendardVariable.woff2) - 제목 · 영문 라벨 서체: Plus Jakarta Sans —
font-display클래스로 적용
app/ 라우트 (page.tsx 는 데이터 조립만 담당)
members/layout.tsx 구성원 3개 페이지 공통 상단(타이틀 + 탭)
components/
layout/ Header, Footer, PageHero, Section
ui/ Container, SectionHeading, Tag, Reveal, LangToggle, EmptyState
home/ 홈 전용 섹션 (HeroMap 등)
people/ 교수 프로필, 구성원 카드 목록
research/ Projects · Publications 페이지 (ProjectList 는 홈과 공유)
contact/ Contact 페이지 본문
content/ 모든 텍스트 데이터 (한/영) + world-land.json
scripts/ 지도 데이터 생성 (npm run world:land)
lib/i18n.tsx 언어 Context + useT() 훅
public/images/ 이미지
public/images/ 안의 이미지는 .gitignore 로 저장소에서 제외되어 있습니다 (개인정보).
폴더 구조는 .gitkeep 으로만 유지되고, 실제 사진은 로컬에만 있습니다.
사진이 없어도 사이트는 정상 동작합니다 — photo: null 이면 이름 첫 글자 플레이스홀더가 표시됩니다.
이 저장소를 GitHub 에 올리고, 로컬에서 빌드한 결과물만 gh-pages 브랜치로 보냅니다.
main 에는 소스만 남고 사진과 빌드 결과물은 커밋되지 않습니다.
git remote add origin https://github.com/<계정>/<저장소>.git # 처음 한 번만
npm run deploynpm run deploy 가 알아서 하는 일:
origin주소에서 저장소 이름을 읽어 basePath 를 자동으로 정합니다- 그 basePath 로
next build실행 →out/ out/을gh-pages브랜치로 강제 push
처음 한 번은 저장소 Settings → Pages 에서 이렇게 지정해 주세요.
| 항목 | 값 |
|---|---|
| Source | Deploy from a branch |
| Branch | gh-pages / (root) |
https://<계정>.github.io/<저장소>/ 처럼 하위 경로로 열리면 모든 자산 경로 앞에 저장소 이름이 붙어야 합니다.
안 붙이면 CSS·JS·사진이 전부 404 가 됩니다. 저장소 이름이 <계정>.github.io 이거나 커스텀 도메인이면
basePath 없이 빌드합니다. 직접 지정하려면 NEXT_PUBLIC_BASE_PATH=/저장소이름 npm run build 로도 됩니다.
next/image 는 images.unoptimized: true 일 때 src 를 그대로 내보내 basePath 가 붙지 않으므로,
이미지 경로는 lib/asset.ts 의 asset() 을 거쳐 씁니다.
public/.nojekyll파일은 지우면 안 됩니다. GitHub Pages 는 기본으로 Jekyll 을 돌리는데, Jekyll 은_로 시작하는 폴더를 무시합니다. Next.js 결과물은_next/라서 이 파일이 없으면 사이트가 통째로 깨집니다.gh-pages브랜치에는 사진이 들어갑니다. GitHub Pages 는 파일이 저장소 안에 있어야 서비스할 수 있어서 피할 수 없습니다. 무료 계정은 public 저장소에서만 Pages 를 쓸 수 있으니, 사진이 공개되면 안 되는 상황이면 Pages 대신 업로드형 호스팅(Netlify CLI 등)을 쓰는 편이 맞습니다.- 이미지 파일은 환경변수(
.env)에 넣을 수 없습니다. 환경변수는 문자열만 담습니다. 사진을 소스에서 빼는 방법은.gitignore+ 위의 배포 방식 조합입니다. - 정적 내보내기에서는
next/image서버 최적화가 동작하지 않아 원본이 그대로 나갑니다.public/images/에 넣기 전에 가로 1000px 내외로 줄여 주세요. (교수님 사진은 4.4MB → 60KB 로 줄여 둔 상태이고, 원본은IPS/material/에 있습니다.) - 빌드 결과를 로컬에서 미리 보려면
npx serve out로 열면 됩니다. 단 basePath 를 넣어 빌드한 결과물은 하위 경로에서만 정상 동작합니다.
- 졸업생·대학원생 명단 — 아직 없어 안내 문구만 표시됨. 생기면
alumni.json·students.json에 추가 - 신규 연구사업 3건(개인정보보호위원회 / 홍익대 / 한국전자기술연구원)의 진행 기간 —
content/projects.ts상단 TODO - 연구실 상세 호실 번호
- 연구실 대표 전화번호 —
content/site.ts의tel이 현재(031) 000-0000임시값 - 영문 성함 확인 —
Jongseong Kim(교수),Heeseo Kim(김희서),Seoyoung Song(송서영) 은 추정 표기