학습 프로젝트
LALA
웹 애플리케이션
GIT - https://github.com/blank09114/LALA
2026.01.07. 20:39
프로젝트 개요
LALA는 사용자가 직접 강의를 개설할 수 있는 참여형 E-러닝 웹 플랫폼입니다.
- 개발 형태: Spring Boot 웹 애플리케이션
- 개발 인원: 6인
- 기술 스택
- 프론트엔드: HTML, CSS, JavaScript, Thymeleaf
- 백엔드: Java
- 데이터베이스: MySQL
- 프레임워크&라이브러리: Spring Boot, Spring Security, Lombok
- 배포: AWS(EC2, RDS, S3)
- 협업/개발 도구: Docker, Git, GitHub, Figma
→ 성과 자료
메인 페이지

메인 페이지는 사용자가 서비스에 진입했을 때 가장 먼저 접하는 화면으로, 단순한 강의 목록 출력이 아니라 사용자 맞춤 데이터와 학습 현황을 동적으로 반영하는 데 초점을 맞췄습니다.
- 템플릿 엔진 연동(Thymeleaf)
- 사용자 닉네임, 프로필 이미지, 추천 강의 리스트를 서버에서 받아와 <span th:text="${user.userNickname}">, <div th:replace="shared/lecList :: lecList(${lecList})"> 같은 방식으로 뿌림
- 프로필 이미지가 없을 경우 기본 이미지를 출력하도록 th:if 조건문 처리
- 학습 캘린더
- updateCalendarUI() 실행 시 /api/course/main/data?startDate=yyyy-MM-dd 형태로 주간 데이터를 비동기로 요청
- 응답 데이터를 renderCalendarLarge()와 renderPlannerGoal()에서 DOM에 반영 → 요일별 일정과 주간 목표가 즉시 출력되도록 구현
- 일정이 없을 경우에는 “일정이 없습니다.”라는 안내 메시지를 표시해 빈 화면 상태를 방지
- 최근 시청 강의
- renderRecentLecture() 함수로 최근 학습한 강의 정보를 카드 형태로 노출
- 강의 제목, 진도율, 프로그레스 바(.progressBar2)를 실시간 갱신
- 강의가 없는 경우에는 display: none 처리하여 UI가 비지 않도록 제어
- 로그아웃 처리
- logOut() 함수에서 /auth/logout API를 호출하고 성공 시 로그인 화면으로 리다이렉트
- 네트워크 실패 시 콘솔 에러를 출력해 디버깅 가능하게 구현
→ 이 구조 덕분에 메인 페이지는 정적 UI + 동적 데이터 로딩이 결합된 형태로 완성되었습니다.
강의 리스트

강의 리스트 페이지는 단순한 목록 나열이 아니라, 카테고리/필터링/검색 결과/지식제공자 캐러셀이 모두 결합된 형태로 구현했습니다. 이 과정에서 프론트엔드에서 데이터를 동적으로 바인딩하고 UI 상태를 제어하는 부분을 중점적으로 맡았습니다.
- 카테고리 네비게이션
- 상단 네비게이션 바에서 카테고리를 클릭하면 data-category 값을 읽어 filterState.categoryId를 변경
- Fetch API로 /api/course/lectures/filtered?categoryId=... 요청을 보내고, 응답 데이터를 renderLectures()를 통해 동적으로 재렌더링
- 선택된 카테고리는 .selected 클래스로 강조 표시하여 상태가 시각적으로 유지되도록 구현
- 필터링 기능
- 가격 필터(무료/유료/할인)와 난이도 필터(입문~고급)를 각각 버튼 및 <select> 요소로 구현
- 클릭/선택 이벤트 발생 시 filterState를 갱신하고, fetchFilteredLectures() 호출 → 조건에 맞는 데이터만 다시 가져오도록 구성
- 할인 강의의 경우, 원가+할인율+할인가가 함께 표시되도록 동적 HTML 생성(regPrice, salePrice 처리)
- 검색 연동
- URL 파라미터(?keyword=)를 읽어 filterState.keyword에 바인딩
- 검색 시 /api/course/lectures/search?keyword=... 엔드포인트 호출, 검색 초기화 버튼으로 파라미터 제거 후 전체 강의 다시 표시
- 검색 결과가 있으면 "검색 결과" 헤더가 노출되고, 없으면 숨김 처리
- 강의 렌더링 구조
- renderLectures()에서 DocumentFragment를 활용해 강의 4개 단위로 Row를 구성 → DOM 성능 최적화
- 각 강의 카드는 썸네일, 제목, 강사명, 가격, 리뷰/수강생 수, 난이도(mapDifficulty())가 포함
- 카드 클릭 시 goToDetail() 함수에서 /course/detail/{id}로 라우팅
- 지식제공자 캐러셀
- /api/course/main/data 호출로 정보 제공자 리스트를 가져와 renderProviders()에서 DOM 동적 구성
- 좌/우 버튼 클릭 시 scrollBy로 부드럽게 이동(scroll-behavior: smooth)
- 양쪽 끝에서는 버튼이 사라지도록 updateBtn()로 상태 관리
- 페이지네이션
- 한 페이지에 40개 강의, 상단/하단 각각 20개씩 나눠서 표시
- renderPagination() 함수에서 총 페이지 수를 계산하여 페이지 버튼 동적 생성
- 버튼 클릭 시 currentPage를 변경하고 fetchFilteredLectures()를 다시 호출
→ 강의 리스트 페이지는 필터링+검색+캐러셀+페이징을 모두 포함하고 있어, 단순 목록 출력이 아닌 사용자 맞춤형 탐색 기능을 제공하는 구조로 완성되었습니다.
강의 상세 페이지
강의 상세 페이지는 강의 소개·목차·리뷰·구매 기능을 하나의 화면에 통합한 구조로, 단순 정적 출력이 아니라 동적 데이터 로딩+상태 관리+사용자 상호작용을 중점적으로 구현했습니다.
- 데이터 로딩 & 바인딩
- /api/course/detail/data/{lectureId} 호출로 강의 상세 정보, 챕터, 리뷰 데이터를 한 번에 로딩
- renderLectureInfo(), renderChapters(), renderReviews() 함수로 각각의 섹션을 DOM에 동적으로 반영
- 강의 제목·소개글·가격·리뷰 평균 점수 등을 실시간으로 바인딩
- 목차 토글 구조
- 각 챕터(chapterId)에 대해 서버에서 /api/course/chapter-contents/{chapterId} 호출 → 동영상·퀴즈·파일 단위 콘텐츠를 불러옴
- toggle(), toggleAll() 함수로 챕터 단위 목차 열기/닫기 제어, 상태에 따라 버튼 라벨도 동적으로 변경
- DOM 초기 상태는 display:none 처리 후, 토글 시 flex로 노출되도록 구현
- 리뷰 시스템
- 리뷰 등록: reviewSubmit()에서 POST /api/course/regReview 호출 → 성공 시 즉시 reload
- 리뷰 수정: editReview() 실행 시 기존 리뷰 DOM을 수정 폼으로 교체 → PUT /api/course/regUpdate로 업데이트
- 리뷰 삭제: 삭제 버튼 클릭 시 모달 확인 후, DELETE /api/course/delReview/{reviewId} 호출 → 성공 시 토스트 메시지 출력
- 리뷰 정렬: 셀렉트 박스 변경 이벤트로 최신순·높은 평점순·낮은 평점순 정렬 후 재렌더링
- 리뷰 페이지네이션: 5개 단위로 그룹핑, showMore()로 추가 리뷰 노출
- 구매 & 장바구니 처리
- handleAddToCart()에서 장바구니 추가 요청 처리 → 버튼 상태가 “장바구니에 담김”으로 즉시 갱신되며 비활성화
- updateCartButton() 함수로 로컬스토리지 상태를 확인해 이미 담긴 경우 중복 방지
- 찜(스크랩) 기능
- toggleLike() 함수로 찜 상태를 서버와 동기화 (POST /bookmarking / DELETE /delBookmark)
- 찜 여부에 따라 아이콘과 토스트 메시지가 즉시 갱신
- 토스트 & 모달 UX
- 삭제 확인은 모달(openModal, closeModal), 상태 알림은 토스트(showToast)로 구현
- 토스트는 2초 후 자동 사라지도록 애니메이션 효과 적용
- 부가 기능
- copyPageUrl()로 현재 강의 URL을 클립보드에 복사 → 성공 시 토스트로 알림
- scrollIntoView({behavior:'smooth'}) 기반으로 강의 소개·목차·리뷰 섹션을 스크롤 이동하는 페이지 핸들러 구현
→ 이 구조 덕분에 강의 상세 페이지는 단순 설명용 화면이 아니라, 사용자 상호작용을 지원하는 완결된 학습/커머스 페이지로 구현되었습니다.
LMS 홈

제가 담당한 LMS 홈은 수강생과 강의 제공자 모두가 이용할 수 있는 강의 관리 대시보드로, 강의 정보와 목차, 사용자 액션(수강 취소, 등록, 수정 등)을 통합 제공하도록 설계했습니다.
- 모달 기반 액션 처리
- 수강 취소 버튼 클릭 시 모달을 열어 확인/취소를 받도록 구현
- JS에서 showModal, closeModal, confirm 함수를 분리해, DOM 상태를 단순히 display:flex/none으로 토글하는 구조로 유지보수성을 높임
- DOMContentLoaded 이벤트에서 버튼 이벤트를 초기 바인딩해, 스크립트 로딩 순서 문제를 피함
- 목차(커리큘럼) 구조 및 토글 기능
- 강좌 목차는 partList 단위로 관리, 각 파트에 동영상·퀴즈·파일 항목을 추가할 수 있도록 함
- 목차는 접고 펼칠 수 있도록 JS에서 toggle과 toggleAll을 구현
- 개별 목차는 버튼 클릭 시 classList display 속성을 토글
- 전체 목차는 Array.from().some()을 사용해 현재 열림 상태를 판단하고 일괄 토글
- 토글 버튼의 아이콘과 상태 문구(“모두 펼치기/모두 접기”)를 연동해 직관적인 UI 제공
- 상태 표시와 편집/추가 기능
- 각 강좌 항목(동영상, 퀴즈, 파일)에 진행 상태(미시청, 미제출, 다운로드 미완료)를 표기해 수강 현황을 바로 확인할 수 있습니다.
- 하단에는 목차 추가, 목차 수정 버튼을 배치하여 강의 커리큘럼을 직접 관리할 수 있도록 했습니다.
- 목차가 하나도 없을 경우에는 index-none 섹션을 노출시켜 “생성된 목차가 없습니다”라는 안내와 추가 버튼만 표시되도록 처리했습니다.
- 스타일링
- 검은색 헤더 + 흰색 콘텐츠 카드 레이아웃으로 시각적 구분
- 모달은 backdrop-filter: blur(5px)를 적용해 반투명 블러 처리로 현대적인 UI 구현
- 목차와 강의 항목은 :hover 효과와 아이콘을 활용하여 대화형 UI로 설계
→ 정리하면, 저는 LMS 홈을 사용자 중심의 대시보드로 설계하여,
- 수강생/제공자별 메뉴 구분,
- 모달 기반 액션 처리,
- 목차 토글 및 상태 표시
이 네 가지 핵심 기능을 중심으로 프론트엔드를 개발했습니다.
강좌 뷰어
강좌

- 강의 영상을 시청할 수 있는 플레이어 화면 제작
- HTML5
- 강좌 정보는 /api/lms/myRecentLecture/chapters/materials/videoDetail/{id} API를 통해 로드
- 영상 시청 화면 옆에 목차 메뉴(lmsMenu) 배치, 다른 강좌·퀴즈·자료로 이동 가능
- 하단에는 지식제공자 코멘트와 QnA 기능을 통합해 학습 중 상호작용이 가능
퀴즈

- 사용자가 강의에 포함된 퀴즈를 풀 수 있는 화면 구현
- 서버에서 퀴즈 데이터(문항, 정답, 해설)를 불러와 렌더링, 제출 이력을 확인해 이전에 제출한 답안 표시
- 객관식은 라디오 버튼, 주관식은 텍스트 입력으로 처리, 제출 시 /api/lms/submit/question API와 연동
- 제출 완료 시 정답 여부와 점수를 바로 확인할 수 있고, 이후에는 수정이 불가능하도록 UI 제어
- QnA 영역을 연동하여, 퀴즈 풀이 후 질문/답변을 등록할 수 있도록 구현
파일 페이지

- 강의에 첨부된 학습 자료를 확인하고 다운로드할 수 있도록 구성
- PDF는 내장 뷰어를 통해 미리보기 가능, 그 외 형식은 파일명/확장자를 표시하고 다운로드 버튼 제공
- 자료 정보는 /api/lms/myRecentLecture/chapters/materials/addiDetail/{id} API로 로드
공용 컴포넌트
- lmsMenu: 강의 목차와 차시별 콘텐츠를 표시하는 메뉴를 fragment로 분리해, 모든 학습 페이지에서 동일한 UI 유지
- lectureBottom: 지식 제공자의 코멘트와 QnA 영역을 공용 fragment로 제작해, 학습자가 어디서든 질문·답변 기능을 사용할 수 있도록 함
배운 점
이번 프로젝트를 통해,
- 학습 서비스에 필요한 강의·퀴즈·자료 뷰어 UI를 직접 구현하며 LMS 전반의 흐름을 이해할 수 있었습니다.
- 영상 플레이어 커스텀 컨트롤러(재생/일시정지, 배속, 전체 화면 등)를 제작하며 HTML5 <video> API와 DOM 조작에 대한 경험을 쌓았습니다.
- 퀴즈 제출, QnA, 학습 진도 관리 등 프론트엔드와 백엔드 API 연동 방식을 익히고, 실제 서비스에 가까운 학습 환경을 구축했습니다.
- 공통 컴포넌트(lmsMenu, lectureBottom)를 fragment로 분리하면서 UI 재사용성과 유지보수성의 중요성을 체득했습니다.
아쉬운 점과 개선 아이디어
- 설계에 많은 시간을 투자하다 보니 실제 구현 단계에서 일정이 촉박해졌습니다.
→ 이후에는 설계와 구현의 균형을 잡아 더 현실적인 일정 관리가 필요합니다.
- 프론트엔드와 백엔드 간 소통 부족: UI 요구사항과 API 구조가 어긋나는 경우가 많아, 구현 중간에 수정 작업에 시간을 추가로 소모했습니다.
→ 이후 프로젝트에서는 초반에 API 명세를 명확히 정의하고, 주기적인 공유 과정을 거쳐 협업 효율을 높이고자 합니다.
- 반응형 UI 미지원: 데스크톱 화면을 기준으로만 작업하여 모바일·태블릿에서 불편할 수 있었습니다.
→ 이후 프로젝트에서는 CSS Grid/Flex, 미디어 쿼리를 적극 활용해 반응형 UI를 적용하고자 합니다.
- CSS 중복 코드 발생: 1 HTML 파일마다 1 CSS 파일을 두는 방식으로 작업하다 보니, 스타일 보일러플레이트가 많이 생겼습니다.
→ 추후에는 컴포넌트화 및 스타일 모듈화를 통해 중복 코드를 줄이고 유지보수성을 개선하고자 합니다.
- 구현 완성도 부족: 짧은 기간 안에 핵심 기능만 우선 구현하다 보니 일부 UI/UX 세부 요소(예: 접근성, 에러 처리)가 부족했습니다.
→ 점차 기능을 확장하면서 사용자 경험을 다듬어 나가는 접근이 필요합니다.