본문 바로가기
세미 프로젝트

세미프로젝트 피드백

by teg0 2025. 11. 21.

세미프로젝트 아이콘

세미프로젝트를 돌아보면서

진행하는 동안의 문제 또는 잘한 점, 완성된 프로젝트의 부족한 점 등 피드백하는 글입니다.

DB 관리자 1로써 DB

 

먼저 사용한 언어와 IDE 등을 소개하며 진행한 순서에 맞게 소개하고 세미 프로젝트의 마침표를 내릴 것이다.

(사실 더하고 싶지만.)

 

Front-End : HTML, CSS, JavaScript, JQuery, BootStrap

Back-End :  Java, Mybatis 

DB : Oracle, SQL Developer

Server : Apache Tomcat

VCS : Git, GitHub, SourceTree


이렇게 서버 - 클라이언트를 통해 Ajax 비동기 통신을 사용하고 서버에서는 스프링 부트 MVC 패턴을 적용하여 Contoller, Service, Mapper, DB, Tomcat을 통해 데이터를 단방향 통신으로 할 생각이다.

 

진행한 일정을 최대한 지키며 계획 - 분석 - 설계 - 개발 - 테스트 - 종료 순으로 진행했다.

 

계획 단계에서 심층적인 회의를 거쳐 '대학병원 검사 시설 관리 ERP 시스템 구축'을 최종 주제로 선정했습니다.

현재 대학병원과 같이 규모가 큰 병원에서는 MRI와 같은 고가 검사 시설을 운영하고 있습니다. 그러나 보안 및 개인 정보 보호를 이유로 환자가 병원의 검사 일정을 직접 투명하게 확인하고 예약할 수 없는 구조적 문제가 있습니다.

 

예를 들어, 환자 A가 MRI 검사를 예약할 때, 의사 또는 시설 관리자와 환자를 중재하는 간호사의 역할이 필수적입니다. 이 과정에서 간호사의 반복적인 일정 조율은 환자와 의사 모두에게 답답함을 유발하며, 간호사의 업무 부담을 가중시키는 주요 원인이 됩니다.

그러므로 저희 조는 기존 ERP에 환자가 직접 검사 일정을 투명하게 확인하고 예약할 수 있는 차별화된 기능을 추가하여 이 문제를 해결하고자 합니다. 이를 통해 간호사의 업무 부담을 획기적으로 감소시키고, 병원과 환자 간의 신뢰도를 높여 신속하고 정확도 높은 일처리를 실현할 수 있습니다.

 


 

다음은 분석 단계에서는 ERD Cloud를 통해 어떤 데이터가 필요한지 구상하였다.

 

저희 시스템은 Member 테이블을 중심으로 환자와 직원의 공통 인적사항을 통합 관리합니다. 이 테이블에서 id와 password 필드가 Null인 경우 비회원, 아니면 회원으로 구분하며, staff_no 필드의 Null 여부에 따라 환자 또는 직원을 구분합니다.


직원은 Staff 테이블에 연결되어 있으며, 다시 의사, 간호사, 병원 시설 관리자 테이블과 연결되어 직종별 상세 정보를 저장합니다. 이들 테이블은 부서 테이블을 참조하여 소속 부서를 관리합니다.

환자의 서비스 이용과 관련된 주요 테이블로는 특정 방문 횟수에 따라 등급을 조정하는 등급 테이블, 환자의 예약 상태를 관리하는 환자 진료 상태 테이블이 있습니다. 예약 유무를 구별하는 예약 테이블과 방문 테이블은 환자의 병원 방문 이력을 관리하며, 방문 이후 진료 과정을 저장하는 진료 기록 테이블로 연결됩니다.

또한, 시설 관리를 위해 전체 시설 정보를 담는 시설 테이블과 이 시설을 예약한 정보를 저장하는 시설 예약 테이블로 구성되어 있습니다.

직원 업무 지원을 위해 문의사항과 답변 테이블, 직원 및 환자 필터링이 적용된 공지사항 테이블, 그리고 직원의 근무 시간을 관리하는 스케줄 테이블근태 테이블이 서로 참조하며 연결되어 있습니다.

 


 

초반 필요한 데이터를 구상을 끝내자마자 필요한 UI가 많기 때문에 Figma 툴을 이용하여, 웹 디자인을 협업을 통해 진행한다.

아무래도 홈페이지와 ERP 모두 제작하고 기능을 넣어야 하기 때문에, 시간이 충분하지 않다.

 

전체 UI 이미지

 

몇 가지만 보여주자면(잘 만든 UI만.),

012345
Figma를 이용한 UI 초안.

 

Figma를 처음으로 사용했지만, 포토샵을 조금 배운 적이 있기 때문에 구상하는데 어려운 점은 없었습니다. 협업으로 조원들과 함께 디자인한 UI들을 코드로 변환하여, 직접 맵핑을 하고 실행했습니다. Figma는 아직 좋긴 하지만, 코드로 가져오는 중에 디자인이 어긋나거나 사라지는 등의 문제가 있기에 수정 작업이 필요했습니다.

 


 

다음은 맵핑이다.

맵핑은 클라이언트에서 요청을 보내면, 스프링이 요청을 읽고 Controller에 같은 url 주소가 존재하는지 확인한 후, 존재한다면 Controller에 연결하여 다음 페이지로 이동하기 위해 완성된 페이지를 응답합니다. 응답한 페이지는 클라이언트에 그려주는 방식인 JSP를 사용했습니다.


 

맵핑을 통해 모두 연결되었다면, 기능을 각 페이지에 넣어야 한다.

이 부분이 가장 많은 시간을 소모했으며, 예상에 맞게 부족하여 몇 가지 기능은 제외했다.

내가 한 기능들을 정리해 보았다.

 

로그인 기능,

회원가입(아이디 중복 확인, 비밀번호 일치 여부) 기능,

검사실 관리 페이지,

직원 관리 페이지

 

홈페이지 UI 중 로그인 페이지.

 

먼저 로그인 기능은 수업시간에 배운 로그인 기능을 응용했습니다. 로그인은 크게 환자와 직원을 나눴습니다.

사용자가 아이디와 비밀번호를 입력하고 DB에 요청을 보냅니다. 이때 Mapper에서 

Select * 
From Member 
where Staff_No is Null 
	and member_id = #{memberId} 
    and member_Pwd = #{memberPwd}
    and Member_Status = 'T';

mybtis 프레임워크를 사용했기 때문에 #을 통해 Mapper 클래스에 전달받은 후 Oracle DB에 위에 적힌 쿼리문을 통해 사용자 정보를 가져옵니다. 사용자 정보는 세션 스토리지에 저장하여, 로그아웃하기 전에 모든 페이지에 적용됩니다.

 

홈페이지 중 회원가입 페이지.

 

두 번째 구현한 기능은 회원가입입니다.

회원가입은 병원에서 사용할 인적사항, 아이디, 비밀번호 등을 적는 공간으로써 Member 테이블에 저장할 정보들을 서버에 보내어 DB에 INSERT 합니다.

INSERT INTO MEMBER (
MEMBER_NO, MEMBER_ID, MEMBER_PWD, MEMBER_NAME, MEMBER_GENDER,
MEMBER_RRN, MEMBER_PHONE, MEMBER_EMAIL, MEMBER_ADDRESS,
MEMBER_JOIN_DATE, MEMBER_BLOOD_TYPE, MEMBER_CHRONIC_DISEASE,
MEMBER_ALLERGY, MEMBER_STATUS
) VALUES(
MEMBER_SEQ.NEXTVAL, #{memberId}, #{memberPwd}, #{memberName}, #{memberGender},
#{memberRrn}, #{memberPhone}, #{memberEmail}, #{memberAddress},
SYSDATE, #{memberBloodType}, #{memberChronicDisease}, #{memberAllergy}, 'T'
);

입력받은 데이터를 모두 저장하고 시퀀스를 사용하여, PK를 사용했습니다. 
아이디와 비밀번호, 전화번호, 주민번호 UNIQUE성별('F', 'M'), 혈액형('A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-')은 check제약 조건을, Member_Status는 기본값을 T로 설정했습니다.

 

회원가입에는 아이디 중복, 비밀번호와 비밀번호 확인 칸의 값이 다를 경우 회원가입 불가 기능을 구현했습니다.

SELECT COUNT(*) 
FROM MEMBER 
WHERE MEMBER_ID = #{memberId} 
	AND MEMBER_STATUS = 'T';

앞서 말씀대로 아이디는 고유의 값을 가집니다. 만약 아이디가 있을 경우 1을 반환하고 없으면 0을 반환합니다.

 

비밀번호 확인 기능은 js로 구현했습니다.

function validationCheck(){
    const pwdInputList = document.querySelectorAll(".signup-form input[type=password]");

    if(pwdInputList[0].value !== pwdInputList[1].value){
        alert("비밀번호가 일치하지 않습니다.");
        return false;
    }
}

회원 가입 폼의 class는 위 코드에 적힌 대로 signup-form입니다. 그 아래에 존재하는 input 태그에서 type이 password인 태그만 가져와 pwdInputList에 저장합니다. 그리고 if문을 통해 리스트에 담겨 있는 값이 다를 경우 alert가 발생하고 false를 반환하여, 회원가입을 실패하게 합니다.

 

검사실 관리 페이지

 

세 번째는 검사실 페이지 기능입니다. 외부 라이브러리 중에서 풀 켈린더를 이용하여 켈린더를 직접 구현하지 않고 CDN(Content Delivery Network)으로 사용자에게 가장 가까운 서버에서 전송하여 속도를 높이고 부하를 줄이는 기술입니다. 즉, CDN은 클라이언트에서 다운로드합니다.

 

첫 번째 헤더는 필터 기능입니다. 전체, MRI, CT, 초음파, X-Ray, 내시경을 누르면 필터를 통해 해당 시설의 일정만 확인할 수 있습니다.

 

두 번째 헤더에 있는 시설 5개를 드래그하거나 날짜를 선택하면, 모달이 뜹니다.

시설 예약을 위한 모달

이 5개 중 하나를 선택하면, 

시설 예약을 위한 모달

시설 예약을 할 수 있는 모달이 사용자에게 보입니다.

예약 시간이 이미 존재할 경우 클릭할 수 없도록 막았습니다.

환자 선택은 모든 환자를 조회하고 내용을 입력한 후 하단에 있는 예약하기를 누르면, DB에 INSERT가 됩니다.

INSERT INTO FACILITY_RESERVATION (
FACILITY_RESERVATION_NO,
RESERVATION_STATUS,
RESERVATION_NOTES,
TREATMENT_DATE,
FACILITY_RESERVATION_MEMO,
STAFF_NO,
MEMBER_NO,
FACILITY_NO
) VALUES (
FACILITY_RESERVATION_SEQ.NEXTVAL,
#{reservationStatus},
#{reservationNotes},
TO_DATE(#{treatmentDate}, 'YYYY-MM-DD HH24:MI:SS'),
#{facilityReservationMemo},
#{staffNo},
#{memberNo},
#{facilityNo}
);

staff_no는 로그인한 직원 번호, member_no는 선택한 환자 번호, facility_no는 사용자가 선택한 시설 번호입니다.

 

풀 켈린더를 넣은 이유와 설명하며, 마지막 기능인 직원 관리 페이지로 넘어가겠습니다.

 

풀 켈린더 라이브러리를 사용하게 된 이유는 켈린더의 기능을 구현할 시간이 부족하며, 더 좋은 풀 켈린더를 알게 되어 스스로 공부하여, ERP 검사실 관리 페이지에 적용했습니다. 풀 켈린더의 학습을 위해 검색과 AI 보조 설명을 통해 학습한 후, 적용했습니다.

 
 
 
 

외부 일정 목록

MRI 가능
CT 가능
X-ray 가능
초음파 가능
내시경 가능
 

풀 켈리더를 정리해 주신 모든 개발자 분들과 AI에게 감사드립니다.

풀 켈린더 공부를 위해 참고한 블로그입니다.

https://junesker.tistory.com/92?category=1238556

 

[FullCalendar] 풀캘린더(FullCalendar) 속성 - 1, 기본 설정 (General Settings)

풀캘린더(FullCalendar) 속성 - 1     FullCalendar JS를 활용하다 보면 굉장히 다양한 속성과 이벤트를 볼 수 있다. 여러 속성들이 어떤 특징들을 가지고 있는지, 풀캘린더가 가지고 있는 이벤트에는

junesker.tistory.com

 

마지막으로 직원 관리 페이지 구현입니다.

직원관리 페이지

ERP 중 제가 처음으로 구현한 페이지입니다.

전체 직원 수, 근무 중인 직원 수, 휴가 중인 직원 수, 퇴사한 직원 수를 DB에 count로 조회합니다.

Ajax는 어떠한 이벤트가 있어야지 데이터를 불러올 수 있습니다. 그래서 사용자가 무언가를 행동하지 않은 이상은 페이지를 새 로고칠 수 없다고 생각했습니다. 그래서 검색으로 찾아본 결과 Polling이라는 기술을 알게 되었습니다.

먼저 폴링 클라이언트가 일정 시간 간격을 두고 주기적으로 서버에 요청을 보내 새로운 데이터가 있는지 확인하는 방법입니다.

하지만, 폴링은 오버헤드가 발생할 수 있는 가능성이 높습니다.

  1. 잦은 연결 및 해체 비용(HTTP HandShacke) : 새로운 HTTP 연결을 만들고 데이터를 받은 후 즉시 연결을 닫는다.
  2. 데이터 변화 없는 불필요한 요청 : 인원수가 변하지 않아도 서버에 요청한다.
  3. 헤더 반복 전송 비용 : 잦은 인증 토큰, 쿠키, 사용자 에이전트 등으로 반복 전송한다.

폴링이 이런 단점이 있다고 알게 되었고 이 단점을 보완한 LongPolling이라는 방법을 알게 되었습니다.

롱폴링은 클라이언트가 요청을 보내면, 서버에 데이터가 실제로 생길 때까지 응답을 미루는(보류하는) 방법입니다.

 

즉, 롱폴링은 데이터가 변화가 있을 경우, 즉시 타임아웃을 하지 않고 데이터를 불러옵니다.

이 롱폴링을 적용하기 위해, 구글과 AI에게 보조를 받아 기능을 구현했습니다.

UI의 사이드바를 통해 다른 페이지를 벗어날 경우 롱폴링이 종료되고 다시 직원 관리 페이지에 들어올 경우, 롱폴링을 시작합니다.

서버에서는 오로지 전송을 위한 전용 객체 DTO를 사용하여, 객체에 데이터를 담고 그 객체를 큐에 담아 선입선출을 통해 클라이언트에 전달합니다. 큐를 생성하는 ConcurrentLinkedQueue 객체를 사용했으며, DTO를 담았습니다.

 

직원 검색과 페이징 처리는 수업시간에 배운 jsp, Ajax, Controller, Service, Mapper를 활용했습니다.

필터 기능 이름, 부서, 직급을 통해 검색할 수 있으며, 페이징 처리 Ajax를 통해, 전체 페이지가 아닌 그 부분만 다시 검색하여, 사용자에게 보여줍니다.

 


이렇게 약 4주 동안 한 결과를 간단하게 소개했습니다.

세미프로젝트 소감이라고 하면, 매우 아쉽습니다.

스스로의 실력, 시간, 지식이 부족하여 완성도가 떨어진 ERP를 구성했습니다.

조원분들의 생각은 모르겠지만, 저는 아쉽고 미련이 남습니다.

좀 더 자원을 투자했다면, 더 완벽했을까?라고 생각합니다.

 

저는 미련을 없애고 후회하지 않기 위해 무엇이든 완벽을 추구합니다.

그러므로 다음 파이널에는 좀 더 자원을 투자하고 협업하여, 스스로 만족 높은 프로젝트를 완성하겠습니다.

 

긴 피드백을 읽어 주셔서 감사합니다.

 

<!DOCTYPE html>
<html lang='ko'>
    <head>
        <meta charset='utf-8' />
        <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js'></script>
        
        <!-- 부트스트랩 5 테마 -->
        <!-- <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' rel='stylesheet'>
        <link href='https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css' rel='stylesheet'> -->

    </head>

    <body>
        <div id='calendar1'></div>
        <div id='calendar2'></div>
        <div id='calendar3'></div>
        <div id='calendar4'></div> 

        <div id="external-events">
            <p>외부 일정 목록</p>
            <div class="fc-event" data-title="MRI 가능" >
                MRI 가능
            </div>
            <div class="fc-event" data-title="CT 가능">
                CT 가능
            </div>
            <div class="fc-event" data-title="X-ray 가능">
                X-ray 가능
            </div>
            <div class="fc-event" data-title="초음파 가능">
                초음파 가능
            </div>
            <div class="fc-event" data-title="내시경 가능">
                내시경 가능
            </div>
        </div>
        <div id="calendar5"></div>

        <script>
            document.addEventListener('DOMContentLoaded', function() {
                var calendarEl = document.getElementById('calendar1');
                var calendar = new FullCalendar.Calendar(calendarEl, {
                    initialView: 'dayGridMonth', //켈린더 타입
                    timeZone: 'Asia/Seoul', //현재 브라우저의 로컬 시간 적용
                    //timeZone은 3가지 방식 : 1. local, 2. UTC, 3. IANA 시간대 'Asia/Seoul'
                    //local은 서버와 클라이언트 시간대 변환 필요
                    //UTC는 서버와 시간 일치
                    initialDate: '2025-11-14', //첫 화면에 보일 날짜
                    locale: 'ko', //언어 설정
                    weekends: true, //주말 설정 기본값 true
                    height: 'auto', //켈린더 머리글, 바닥글 포함 전체 높이 설정 -> 켈린더 내용에 따라 전체 높이 설정
                    contentHeight: 'auto', //켈린더 내용 높이 설정
                    aspectRatio: 1.5, //켈린더 가로 세로 비율 설정 -> 공식은 너비/높이 = 1.5 : 1
                    // slotMinTime: //하루의 시작 시간 -> dayGridWeek에 사용됨
                    // slotDuration: //시간 슬롯을 표시하는 빈도 -> dayGridWeek에 사용됨
                    now: '2025-11-14', //달력에 표시될 현재 날짜
                    // businessHours: true, //업무 시간 설정, 기본적으로 오전9시 ~ 오후5시, 하얀바탕이 강조되는 부분 -> dayGridWeek에 사용됨
                    // events: [
                    //     {title : '팀회의', start: '2025-11-14T10:30:00'}
                    // ]
                    firstDay: 0, //기본값 0은 일요일이며 시작도 일요일 요일은(0부터6)
                    event: [
                        {title : 'MRI 가능', start: '2025-11-10'},
                        {title : 'MRI 가능', start: '2025-11-11'},
                        {title : 'MRI 가능', start: '2025-11-12'},
                        {title : 'MRI 가능', start: '2025-11-13'},
                        {title : 'MRI 가능', start: '2025-11-14'}
                    ],
                    // validRange: //사용자가 탐색할 수 있는 날짜와 이벤트가 이동할 수 있는 위치를 제한
                    
                });
                calendar.render();
            });

            document.addEventListener('DOMContentLoaded', function() {
                var calendarEl = document.getElementById('calendar2');
                var calendar = new FullCalendar.Calendar(calendarEl, {
                    initialView: 'dayGridMonth', //켈린더 타입
                    events: function(fetchInfo, successCallback, failureCallback) {
                        $.ajax({
                            usl: '/api/getEvents',
                            dataType: 'json',
                            data: {
                                start: fetchInfo.startStr,
                                end: fetchInfo.endStr
                            },
                            success: function(response) {
                                successCallback(response); //일정 로드 완료
                            },
                            error: function() {
                                failureCallback(); //에러 처리
                            }
                        });
                    }
                });
                calendar.render();
            });

            document.addEventListener('DOMContentLoaded', function() {
                var calendarEl = document.getElementById('calendar3');
                var calendar = new FullCalendar.Calendar(calendarEl, {
                    initialView: 'dayGridMonth', //켈린더 타입
                    eventColor: '#DCFCE7',
                    eventTextColor: '#000000',
                    eventBorderColor: '#FFFFFF',
                    editable: true, //드래그 및 크기 조정 가능
                    dayMaxEvents: 5, //하루에 최대 3개의 이벤트만 표시

                    // moreLinkClick: function(info) {
                    //     alert('더보기 클릭: ' + info.date);
                    //     return false;
                    // }, 팝업 창 열기 등 기능 추가 가능

                    // evnetOverlap: function(stillEvent, movingEvent) {
                    //     if(stillEvent.title === movingEvent.title) {
                    //         return false; //겹침 금지
                    //     }
                    //     return true; //나머지 겹침 허용
                    // }, -> week에서 사용

                    headerToolbar: {left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay'},
                    //titleFormat: {year : 'numeric', month : '2-digit', day : '2-digit'},
                    events: [
                        {
                            title: 'MRI 가능',
                            start: '2025-11-10',
                            end: '2025-11-11',
                            startStr: 'Start Text', // 일정 bar에 출력되지는 않음
                            endStr: 'End Text', // 일정 bar에 출력되지는 않음
                            className: ['custom-className1', 'custom-className2'],
                            editable: false, //드래그 및 크기 조정 불가능
                            display: 'block', //직사각형 bar가 나타남
                            overlap: false, //이벤트와 이벤트 겹치지 않게 설정
                            backgroundColor: '#DCFCE7',
                            textColor: '#000000',
                            borderColor: '#FFFFFF',
                            
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-11',
                            end: '2025-11-12'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-12',
                            end: '2025-11-13'    
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-13',
                            end: '2025-11-14'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-14',
                            end: '2025-11-15'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-14',
                            end: '2025-11-15'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-14',
                            end: '2025-11-15'
                        },
                    ]
                });
                calendar.render();
            });

            document.addEventListener('DOMContentLoaded', function() {
                var calendarEl = document.getElementById('calendar4');
                var calendar = new FullCalendar.Calendar(calendarEl, {
                    initialView: 'dayGridMonth', //켈린더 타입
                    headerToolbar: {left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay'}, // 헤더 종류
                    editable: true, //드래그 및 크기 조정 가능
                    dayMaxEvents: 3, //하루에 최대 3개의 이벤트만 표시
                    selectable: true, //선택 가능
                    // unselectAuto: true, //다른곳 선택시 현재 선택이 지워짐
                    selectConstraint: {
                        startTime: '09:00',
                        endTime: '18:00'
                    },
                    selectMirror: true, //드래그 하는 동안 placeHolder 이벤트를 그릴지 여부, week, day만 가능
                    events: [
                        {
                            title: 'MRI 가능',
                            start: '2025-11-10',
                            end: '2025-11-11',
                            startStr: 'Start Text', // 일정 bar에 출력되지는 않음
                            endStr: 'End Text', // 일정 bar에 출력되지는 않음
                            className: ['custom-className1', 'custom-className2'],
                            editable: false, //드래그 및 크기 조정 불가능
                            display: 'block', //직사각형 bar가 나타남
                            overlap: false, //이벤트와 이벤트 겹치지 않게 설정
                            backgroundColor: '#DCFCE7',
                            textColor: '#000000',
                            borderColor: '#FFFFFF',
                            
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-11',
                            end: '2025-11-12'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-12',
                            end: '2025-11-13'    
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-13',
                            end: '2025-11-14'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-14',
                            end: '2025-11-15'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-14',
                            end: '2025-11-15'
                        },
                        {
                            title: 'MRI 가능',
                            start: '2025-11-14',
                            end: '2025-11-15'
                        },
                    ]
                });
                calendar.render();
            });

            document.addEventListener('DOMContentLoaded', function() {
                var calendarEl = document.getElementById('calendar5');
                var calendar = new FullCalendar.Calendar(calendarEl, {
                    initialView: 'dayGridMonth', //켈린더 타입
                    headerToolbar: {left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay'}, // 헤더 종류
                    editable: true, //드래그 및 크기 조정 가능
                    dayMaxEvents: 3, //하루에 최대 3개의 이벤트만 표시
                    dropable: true, //드롭 가능
                    
                    eventClick: function(info) {
                        alert('삭제: ' + info.event.title);
                        info.event.remove();
                    },

                    eventDrop: function(info) {
                        alert('일정 클릭: ' + info.event.title);
                    },

                    eventResize: function(info) {
                        alert('일정 클릭: ' + info.event.title);
                    }
                });
                calendar.render();
            });

            document.addEventListener('DOMContentLoaded', function() {
                new FullCalendar.Draggable(document.getElementById('external-events'), {
                    itemSelector: '.fc-event', //드래그 가능 요소
                    eventData: function(eventEl) {
                        return {
                            title: eventEl.dataset.title
                        };
                    }
                });
            });
        </script>
    </body>
</html>

 

'세미 프로젝트' 카테고리의 다른 글

세미프로젝트 15일차  (0) 2025.11.13
세미프로젝트 11일차  (0) 2025.11.07
세미 프로젝트 8일차  (0) 2025.11.04
세미 프로젝트 1일차 ~ 4일차  (0) 2025.10.28