개발 낙서장

[TIL] 내일배움캠프 2일차 - 모달 - 본문

Java/Sparta

[TIL] 내일배움캠프 2일차 - 모달 -

권승준 2023. 12. 22. 19:35

페이지 완성본✅

https://seungjun-kwon.github.io/Sparta/

 

나만의 추억 앨범

 

seungjun-kwon.github.io

오늘의 학습 키워드📚

모달

기존에 했던 앨범 페이지에서 앨범의 버튼을 누르면 새로운 팝업 창이 뜨고 거기에서 앨범이 어떤 내용인지 자세히 볼 수 있는 창, 그리고 수정하기 버튼을 누르면 수정이 가능케 하고 비밀 번호를 입력하여 새로 저장하는 기능을 만들고자 했다.새로 html을 띄우는 것보단 해당 페이지에서 자연스럽게 나타낼 뭔가가 없을까 생각해 보고 검색해 보다가 모달이라는 것을 알게 됐다.https://getbootstrap.kr/docs/5.3/components/modal/#%EC%98%88%EC%8B%9C

 

모달

Bootstrap JavaScript 모달 플러그인을 사용하여 라이트박스, 사용자 알림 또는 사용자 정의 콘텐츠를 만들 수 있습니다.

getbootstrap.kr

부트스트랩에서 다양한 모달을 체험해보고 코드를 가져와 사용할 수 있다.

나는 여러 입력값들과 수정하기, 저장하기 버튼이 필요했기에 해당 모달을 사용했다.

모달과 버튼을 그대로 가져와서 사용해보니 아무런 반응도 없었다. 대체 뭐가 문제일까 고민하고 검색해도 방법을 찾기가 힘들어 스파르타코딩클럽의 학습 질문 커뮤니티에 질문했다.

튜터님께서 바로 답변해주셨는데 부트스트랩의 js관련 cdn을 추가해야 된다고 하셨다.

  • CDN이란?
    서버에 있는 콘텐츠들을 사용자들에게 배포하기 위해 지리적으로 분산된 네트워크를 말한다.

잘 이해가 되진 않았지만 부트스트랩에서 제공하는 여러 콘텐츠들을 사용하기 위한 규약(?)이라고 이해하고 사용했다.

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
	integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
    crossorigin="anonymous"></script>

아무튼 위의 코드를 html의 <head> 부분에 추가하고 실행하면 아주 잘 동작한다.

그럼 나는 이 모달을 조금 수정해줘야 하는데 처음 앨범 상세 조회 버튼을 누르면

앨범 사진
앨범 제목
앨범 내용
닫기 / 수정하기

이런 폼의 모달이 등장해야 하고 수정하기 버튼을 누르면

앨범 사진 Input
앨범 제목 Input
앨범 내용 Input
비밀번호 Input
닫기 / 저장하기

모달이 이렇게 바뀌어서 수정 및 저장이 가능하도록 만들어야 한다.

먼저 기본 앨범을 나타내주는 HTML 코드를 수정해 상세 보기 버튼을 추가해줘야 한다.

            <div class="col">
                <div class="card h-100">
                    <img src="${image}"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">${title}</h5>
                        <p class="card-text text-hidden">${content}</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">${date}</small>
                    </div>
                    <button class="open-album" type="button" data-bs-toggle="modal" data-bs-target="#album-modal" data-album-id="${doc.id}">상세 보기</button>
                </div>
            </div>

마지막에 버튼을 추가했고 함수 사용을 위해 id 속성은 중복이 되면 안 되니 class 속성을 달아주었다.
그리고 버튼을 눌렀을 때 모달에 앨범의 정보를 전달해줘야 하니 고유 id를 data로 넘겨주었다.
해당 정보들은

 

Dictionary 변수를 선언하여 Key는 고유 id, Value는 해당 앨범의 정보가 담긴 Dictionary로 담아주었다.

    <div class="modal fade" id="album-modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h1 class="modal-title fs-5" id="modal-header">앨범 조회</h1>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form>
                        <div class="mb-3">
                            <label for="modal-image" class="col-form-label">사진</label>
                            <image src="" class="form-control" id="modal-image"></image>
                            <input type="text" class="form-control" id="modal-image-input">
                        </div>
                        <hr>
                        <div class="mb-3">
                            <label for="modal-title" class="col-form-label">제목</label>
                            <label type="text" class="form-control" id="modal-title-label"></label>
                            <input type="text" class="form-control" id="modal-title-input">
                        </div>
                        <hr>
                        <div class="mb-3">
                            <label for="modal-content" class="col-form-label">내용</label>
                            <label type="text" class="form-control" id="modal-content-label"></label>
                            <input type="text" class="form-control" id="modal-content-input">
                        </div>
                        <hr>
                        <div class="mb-3" id="modal-password">
                            <label for="modal-image" class="col-form-label">비밀번호</label>
                            <input type="password" class="form-control" id="modal-password-input">
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
                    <button type="button" class="btn btn-primary" id="modal-edit-btn">수정하기</button>
                    <button type="button" class="btn btn-primary" id="modal-save-btn">저장하기</button>
                </div>
            </div>
        </div>
    </div>

모달의 HTML 부분도 수정해줬는데 각각 수정을 위한 Input을 추가해 주었고 해당 부분들은 JQuery에서 toggle 함수로 껐다 켰다 해주면 된다.

        function modalToggle(flag) {
            if (flag == true) {
                $('#modal-title-label').css('display', '');
                $('#modal-title-input').css('display', 'none');

                $('#modal-content-label').css('display', '');
                $('#modal-content-input').css('display', 'none');

                $('#modal-image').css('display', '');
                $('#modal-image-input').css('display', 'none');

                $('#modal-password').css('display', 'none');

                $('#modal-save-btn').css('display', 'none');
                $('#modal-edit-btn').css('display', '');
            }
            else {
                $('#modal-title-label').css('display', 'none');
                $('#modal-title-input').css('display', '');

                $('#modal-content-label').css('display', 'none');
                $('#modal-content-input').css('display', '');

                $('#modal-image').css('display', 'none');
                $('#modal-image-input').css('display', '');

                $('#modal-password').css('display', '');

                $('#modal-save-btn').css('display', '');
                $('#modal-edit-btn').css('display', 'none');
            }
        }

먼저 모달 요소들을 Toggle 해주는 함수이다, 파라미터로 boolean값을 받아서 true일 경우 처음 상세 보기 창을 띄웠을 때이고 false일 경우 수정하기 버튼을 눌러 Input으로 바뀌게 해 주었다.

        $('.open-album').click(async function () {
            var albumData = albumDic[$(this).data('album-id')];

            modalToggle(true);

            $('#modal-title-label').text(albumData['title']);

            $('#modal-content-label').text(albumData['content']);

            $('#modal-image').attr('src', albumData['image']);
        })

        $('#modal-edit-btn').click(async function () {
            modalToggle(false);

            $('#modal-title-input').val($('#modal-title-label').text());

            $('#modal-content-input').val($('#modal-content-label').text());

            $('#modal-image-input').val($('#modal-image').attr('src'));

            $('#modal-password').val('');
        })

각각 상세 보기 버튼, 수정하기 버튼을 눌렀을 때 이벤트이다.

$(this).data를 통해 현재 요소에 담긴 data 값을 가져오는데 앨범에 고유 id값을 저장해 Dictionary에 Key값으로 저장해 두었으니 데이터를 꺼내서 사용하면 된다.

왼쪽이 앨범의 상세 보기 버튼을 눌렀을 때 나타나는 모달, 오른쪽이 해당 모달에서 수정하기 버튼을 눌렀을 때 바뀌는 모달이다.
이제 앨범 정보에서 사용자 이름과 비밀번호를 FirestoreDB에 등록하고 날짜는 입력받는 것이 아닌 등록 및 수정 기준 날짜로 자동으로 저장되게 바꾸면 된다.

우선 등록할 때 비밀번호를 받으려면 비밀번호 재입력 칸을 만들고 유효성 체크를 해야 한다.
비밀번호 체크를 하는 김에 모든 입력값에 유효성 체크를 하는데 이미지 입력란에는 반드시 이미지 주소가 들어가야 한다.

/\.(png|jpg|jpeg|gif)(\?.*)?$/.test(url)

검색해 보니 위와 같은 유효성 검사 방법이 있었다.
특정 문자열이 어떻게 이루어져 있는지를 체크하는 방법인데 이미지 주소여야 하므로 확장자 등을 추가하여 검사했다.

        function checkInput(image, title, content, name, password, passwordRe) {
            if(!image.trim() || !title.trim() || !content.trim() || !name.trim() || !password.trim() || !passwordRe.trim()) {
                alert('빈 값을 채워주세요.');
                return false;
            }

            if(/\.(png|jpg|jpeg|gif)(\?.*)?$/.test(image) == false) {
                alert('이미지 주소를 확인해주세요.');
                return false;
            }

            if(password.trim() != passwordRe.trim()) {
                alert('비밀번호가 같은지 확인해주세요.');
                return false;
            }

            return true;
        }

함수를 만들었고 파라미터로는 입력값들을 받았다. 빈 값이 있는 경우, 이메일 주소가 아닌 경우, 비밀번호 확인이 일치하지 않는 경우로 나누어 검사해 주었다.
boolean을 반환해 언제든지 체크하고 값을 확인할 수 있게 했다.

 

이제 수정 기능을 만들면 된다.
수정 기능은 생성과 거의 다를 게 없다. FirestoreDB에 생성은 addDoc을 사용했다면 수정은 updateDoc을 사용하면 된다.
다만 updateDoc은 해당 파이어스토어 문서의 고유 ID가 필요하기 때문에 미리 캐싱해줘야 한다.

        $('.open-album').click(function () {
            currentId = $(this).data('album-id');
            let albumData = albumDic[currentId];

            modalToggle(true);

            $('#modal-writer').text('작성자 : ' + albumData['name']);
            $('#modal-title-label').text(albumData['title']);
            $('#modal-content-label').text(albumData['content']);
            $('#modal-image').attr('src', albumData['image']);
        })

앨범 상세 보기 버튼을 눌렀을 때 동작을 조금 바꿨는데 전역변수로 currentId를 선언해 앨범을 클릭할 때마다 현재 보고 있는 앨범의 id를 넣어주었다.

        async function albumEdit() {
            let image = $('#modal-image-input').val();
            let title = $('#modal-title-input').val();
            let content = $('#modal-content-input').val();
            let name = albumDic[currentId]['name'];
            let password = $('#modal-password-input').val();
            let passwordRe = albumDic[currentId]['password'];

            let today = new Date();
            let year = today.getFullYear(); // 년도
            let month = today.getMonth() + 1;  // 월
            let date = today.getDate();  // 날짜

            if(checkInput(image, title, content, name, password, passwordRe) == false)
                return;

            let data = {
                'image': image,
                'title': title,
                'content': content,
                'date': year + '/' + month + '/' + date,
                'name': name,
                'password': password
            }
            await updateDoc(doc(db, "albums", currentId), data);

            alert('저장 완료!');
            window.location.reload();
        }

이제 수정할 입력값들을 받고 수정해 주면 끝이다.

addDoc(doc(db, "컬렉션 이름", "문서 ID"), 수정할 데이터)

 

import { collection, addDoc, updateDoc, doc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

** 참고로 updateDoc, doc 등 firebase의 js 함수들을 사용하려면 함수를 import해서 라이브러리에서 가져와야 한다.

 

 

다음은 삭제이다.
삭제는 이렇게 구현하려고 한다.
삭제 버튼 ->
비밀번호 입력 창(prompt) ->
맞으면 다시 한번 확인 창(confirm)
틀리면 비밀번호 틀렸다고 알려주기(alert) ->
FirestoreDB에서 삭제(deleteDoc)

prompt('내용') : 확인 혹은 취소 버튼을 눌렀을 때 입력된 값을 문자열로 반환한다.
confirm('내용') : 확인을 누르면 true, 취소를 누르면 false를 반환한다.

deleteDoc(doc(db, "컬렉션 이름", 문서 ID));

추가와 수정을 구현하면서 얻은 지식으로 삭제는 아주 빠르게 만들 수 있었다.

 

어제 목표했던 것들을 모두 달성했다!
앨범에 작성자 항목을 넣어 누가 작성했는지 알 수 있게 하고
비밀번호를 추가해 비밀번호를 알고 있는 사람만 수정할 수 있게 했다.
또 작성된 앨범을 삭제하는 기능도 만들었다.

기능들을 구현하면서 html에 대한 간단한 활용, JS나 JQ의 함수나 문법들에 익숙해지는 시간을 가질 수 있었다.


오늘의 회고💬

오늘은 꽤나 재밌었다. 아직 Spring 백엔드를 공부하는 단계도 아니고 웹 개발의 기초적인 부분들로 간단한 앨범 페이지를 만든 것이지만 새로운 언어를 배우고 활용하면서 결과물이 즉시 내 눈에 보이니까 괜찮았던 것 같다.

하지만 온전히 내 스스로의 힘으로 한 것이 아니라 검색과 튜터님들의 도움이 필요했기에 공부는 계속해야 할 것 같다.

 

내일의 계획📜

이제 곧 Java를 시작하니까 Java의 문법에 대해 익숙해질 필요가 있다는 생각이 들었다.
기존에 코딩 테스트 문제는 주로 C++로 풀었었는데 Java로도 풀어봐야 할 것 같다.

 

Comments