Pyeonggang Church
교회 소개와 방송 콘텐츠, 수련원 예약과 관리자 운영이 함께 있는 홈페이지의 퍼블리싱과 프론트엔드를 맡았습니다. Spring Boot와 Thymeleaf로 구성된 서버에 페이지별 TypeScript 번들과 공통 Sass 스타일을 연결하고 예약 신청과 콘텐츠 화면을 구현했습니다.
핵심 기여
| 과제 | 본인 기여 | 코드에서 확인한 내용 |
|---|---|---|
| 서버 템플릿과 프론트엔드 연결 | 공통 레이아웃과 페이지 초기화 분리 | 공통 header, footer와 수련원 엔트리 분리 |
| 조건이 이어지는 예약 신청 | 날짜와 인원, 호실, 신청자 입력의 단계 연결 | 빈 조회 결과와 필수값, 동의 상태를 분기 |
| 콘텐츠 화면 연결 | 공통 기반 위에 방송과 게시물 화면 구현 | 목록 카드의 상세 이동과 화면 구성 |
개요
| 항목 | 내용 |
|---|---|
| 기간 | 2024.09 ~ 2025.02의 주요 구축과 개선 |
| 역할 | 퍼블리싱과 프론트엔드, 일부 서버 예외 처리와 검증, broadcast API 보조 |
| 협업 | GUI 팀의 디자인을 받아 구현했습니다. 서버 전체를 맡은 역할과는 구분했습니다 |
| 기술 | TypeScript, Webpack, Sass, jQuery, Thymeleaf, Spring Boot |
| 서비스 | 평강교회 홈페이지 |
맡은 범위
공통 헤더와 푸터, 홈과 콘텐츠 영역, 방송과 게시물 상세, 수련원 소개와 예약 신청을 구현했습니다. 관리자 메뉴와 게시물 운영 화면, 예약 상태 변경 UI도 같은 엔트리 구조에 연결했습니다.
문제와 해결
공통 레이아웃과 페이지 초기화를 나누기
문제. 서버 템플릿과 TypeScript가 같은 저장소에 있었고 여러 페이지의 초기화 코드가 공통 엔트리에 모여 있었습니다.
판단. 공통 헤더와 푸터는 main 엔트리에서 초기화하고 페이지별 동작은 해당 엔트리로 나눴습니다. 팀의 Webpack 빌드 구조에 맞춰 화면과 템플릿을 연결했습니다.
구현 코드: 공통 레이아웃과 수련원 엔트리 분리
import "@style/main.scss";
import footer from "@layouts/footer";
import header from "@layouts/header";
header.init();
footer.init();import centerHome from "@page/center-home";
centerHome.init();본인이 엔트리를 분리한 변경에서 발췌했습니다. 출력은 페이지별 JavaScript와 공통 main.css로 서버 템플릿에 연결했습니다.
구현. 수련원 main 엔트리를 추가하고 공통 main 엔트리에서는 페이지 초기화 호출을 걷어냈습니다. 템플릿의 스크립트 연결도 함께 정리했습니다.
결과. 공통 레이아웃과 페이지 동작의 진입점을 구분했습니다. 폴더를 읽어 엔트리를 자동 생성하는 Webpack 로직은 팀이 만든 공유 빌드 구조를 사용했습니다.
예약 조건과 다음 단계의 완료 조건을 연결하기
문제. 예약 날짜와 인원에 따라 가능한 호실을 조회하고 그 결과에서 호실을 고른 뒤 신청자 정보를 받아야 했습니다. 결과가 없거나 입력이 끝나지 않은 상태도 화면에서 설명해야 했습니다.
판단. 날짜와 인원, 호실 선택, 신청자 정보의 세 단계로 나눴습니다. 날짜 범위는 캘린더가 관리하고 각 단계의 완료 조건이 다음 버튼 상태를 결정하게 했습니다.
flowchart LR
Dates["날짜와 인원"] --> Fetch["가능한 호실 조회"]
Fetch --> Empty["빈 결과: 조건 다시 선택"]
Fetch --> Room["호실 선택"]
Room --> Form["신청자 정보와 동의"]
Form --> Submit["예약 신청 요청"]구현. 조회 결과가 없으면 조건을 다시 고를 수 있도록 이전 동작을 제공했습니다. 이름, 전화번호와 동의 항목이 채워졌을 때 다음 단계로 이동하도록 처리했습니다.
구현 코드: 신청자 정보의 완료 조건
const isNameFilled = ($nameInput.val() as string).trim() !== "";
const isPhoneFilled = ($phoneInput.val() as string).trim() !== "";
const isAgreeCollectPhoneChecked = $agreeCollectPhoneCheckbox.prop("checked");
const isAgreeGuidelinesChecked = $agreeGuidelines.prop("checked");
const fulfilled = isNameFilled && isPhoneFilled
&& isAgreeCollectPhoneChecked && isAgreeGuidelinesChecked;당시 화면의 필수값 안내를 발췌했습니다. 전화번호 형식과 예약 충돌에 관한 서버 검증 전체를 뜻하지 않습니다.
결과. 조회 조건과 선택 결과, 신청자 입력이 단계별로 연결됐습니다. 운영 중 발생한 캘린더와 모바일 입력 UI의 수정도 이 흐름 안에서 반영했습니다.
함께 사용한 목록 기반
방송 목록은 URL query와 부분 HTML 갱신, popstate를 사용하는 공유 구현 위에 있었습니다. 이 영역에서 본인이 직접 변경한 내용은 카드의 상세 주소와 콘텐츠 화면 연결입니다. 조회 기반을 사용하는 작업과 그 기반 자체를 만든 역할을 구분했습니다.
결과와 확인 범위
페이지별 빌드, 예약 신청과 방송 목록 코드를 직접 변경한 이력을 기준으로 정리했습니다. 이후 후속 배포 기록은 있지만 그 기록만으로 주요 구축 기간을 연장하지 않았습니다. 후속 유지보수의 정확한 기간과 범위는 확인 필요로 남겼습니다.