개발 회고/TIL

[12/26] TIL

mabubsoragodong 2024. 12. 26. 20:32

😊오늘 배운 내용

팀 프로젝트 - 개인 페이지 구현

SQL 강의 select, from 까지

 

[개인 페이지 구현 코드]

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Team Introduction</title>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

    <style>
        @import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Gowun+Dodum&family=IBM+Plex+Sans+KR&family=Nanum+Pen+Script&display=swap');

        * {
            font-family: "Nanum Pen Script", serif;
            font-weight: 400;
            font-style: normal;
        }

        /* Reset */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-size: 30px;
            background-image: url('https://aniland1.cafe24.com/studio_ghibri/download/img/ponyo.jpg');
            /* 배경 이미지 URL */
            background-position: center center;
            /* 이미지 중앙에 위치 */
            background-size: cover;
            /* 화면 전체를 덮도록 크기 조정 */
            background-repeat: no-repeat;
            /* 이미지 반복하지 않도록 설정 */
            color: #333;
            line-height: 1.6;
        }

        /* Header */
        .header {
            background-color: rgba(174, 198, 207, 0.8);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
            padding: 20px 0;
            position: sticky;
            top: 0;
            z-index: 1000;
        }

        .header nav {
            width: 90%;
            max-width: 1200px;
            margin: 0 auto;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .header nav a {
            font-weight: 600;
            text-decoration: none;
            color: #333;
            margin: 0 15px;
            transition: color 0.2s ease-in-out;
        }

        .header nav a:hover {
            color: #0074ff;
        }

        /* Main Container */
        .container {
            width: 90%;
            max-width: 1200px;
            margin: 40px auto;
        }

        /* Profile Section */
        .profile {
            display: flex;
            gap: 30px;
            align-items: center;
            margin-bottom: 40px;
        }

        .profile .image {
            width: 355px;
            height: 200px;
            border-radius: 8px;
            background-image: url('KakaoTalk_20241226_132325328.jpg');
            background-position: center center;
            background-size: contain;
            /* background: linear-gradient(135deg, #0074ff, #00d1ff);
            border-radius: 12px;
            color: #ffffff;
            font-size: 24px;
            font-weight: bold; */
            display: flex;
            justify-content: center;
            align-items: center;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }

        .profile .info {
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        .profile .info div {
            background-color: rgba(255, 255, 255, 0.8);
            /* 배경을 투명하게 */
            /* background-color: #ffffff; */
            border: 1px solid #e6e8eb;
            border-radius: 8px;
            padding: 15px 20px;
            font-size: 30px;
            font-weight: 500;
            color: black;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
            /* opacity: 0.8; 텍스트 카드에 투명도 적용 */
        }

        /* Details Section */
        .details {
            display: flex;
            justify-content: space-between;
            gap: 20px;
            margin-bottom: 40px;
        }

        .details div {
            display: flex;
            justify-content: center;
            align-items: center;
            flex: 1;

            background-color: rgba(255, 255, 255, 0.8);
            /* 배경을 투명하게 */
            /* background-color: #ffffff; */
            border: 1px solid #e6e8eb;
            border-radius: 8px;
            text-align: center;
            padding: 10px;
            font-size: 40px;
            font-weight: 600;
            color: #333;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
        }

        .details .rating {
            flex: 0.5;
        }

        .details .name {
            font-size: 70px;
        }

        .details .type {
            font-size: 70px;
        }

        /* Abilities Section */
        .abilities {
            background-color: rgba(255, 255, 255, 0.8);
            /* 배경을 투명하게 */
            /* background-color: #ffffff; */
            border: 1px solid #e6e8eb;
            border-radius: 12px;
            padding: 30px 20px;
            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
            margin-bottom: 40px;
        }

        .abilities .title {
            font-size: 40px;
            font-weight: 600;
            color: #333;
            margin-bottom: 20px;
        }

        .abilities .list div {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 20px;


            background-color: #f9fafb;
            border-radius: 8px;
            margin-bottom: 10px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
            font-size: 30px;
            color: #555;
        }

        .abilities .list div span {
            font-weight: 500;
            font-size: 30px;
        }


        .abilities .list div:last-child {
            margin-bottom: 10px;
        }


        .highlight {
            font-weight: bold;
            /* 글자를 굵게 */
            font-size: 60px;
            /* 글자 크기 크게 */
        }

        .form-control {
            font-size: 20px;
            /* 입력 텍스트 크기 */
            height: 50px;
            /* 입력 필드 높이 */
        }

        /* 라벨 높이 기준 가운데 정렬 */
        .form-floating label {
            display: flex;
            align-items: center;
            height: 100%;
            /* 부모 요소 높이 기준으로 정렬 */
            padding-left: 10px;
            /* 좌측 여백 추가 (선택 사항) */
            font-size: 25px;
            /* 라벨 텍스트 크기 */
        }

        .mybtn {
            margin-bottom: 30px;
        }
    </style>

    <script type="module">
        // Firebase SDK 라이브러리 가져오기
        import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
        import { getFirestore } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
        import { collection, addDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
        import { getDocs } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";


        // For Firebase JS SDK v7.20.0 and later, measurementId is optional
        const firebaseConfig = {
            
        };


        // Firebase 인스턴스 초기화
        const app = initializeApp(firebaseConfig);
        const db = getFirestore(app);


        $("#postingbtn").click(async function () {
            let name = $('#name').val();
            let content = $('#content').val();

            // 현재 시간 추가
            let now = new Date();
            let timestamp = now.toLocaleString(); // 시간 형식 변환 (YYYY-MM-DD HH:mm:ss)


            let doc = {
                'name': name,
                'content': content,
                'timestamp': timestamp, // 시간 필드 추가
            };

            await addDoc(collection(db, "profile"), doc);

            alert('방명록을 남겼습니다!');
            window.location.reload();

        })

        let docs = await getDocs(collection(db, "profile"));
        docs.forEach((doc) => {
            let row = doc.data();
            console.log(row);

            let name = row['name'];
            let content = row['content'];
            let timestamp = row['timestamp']; // 저장된 시간 가져오기


            let temp_html = `<li class="list-group-item">${name} - ${content} <span style="float:right;">${timestamp}</span></li>`;

            $('#postinglist').append(temp_html);

        });
    </script>
</head>

<body>
    <header class="header">
        <nav>
            <a href="#">팀 소개 페이지</a>
            <a href="#">팀원 1</a>
            <a href="#">팀원 2</a>
            <a href="#">팀원 3</a>
            <a href="#">팀원 4</a>
            <a href="#">팀원 5</a>
        </nav>
    </header>

    <div class="container">
        <div class="profile">
            <div class="image"></div>
            <div class="info">
                <div><span class="highlight">각오 & TMI</span><br>열심히 해서 백엔드 개발자로 취뽀하자!<br>18살 강아지를 키우고 있어요🐶 </div>
            </div>
        </div>

        <div class="details">
            <div class="name">문유빈</div>
            <div class="type">INTP</div>
            <div class="rating">종합 능력치<br>⭐⭐⭐</div>
        </div>

        <div class="abilities">
            <div class="title">능력치</div>
            <div class="list">
                <div><span>노력 ⭐⭐⭐⭐⭐</span><span>열심히 할 자신 있습니다!!!</span></div>
                <div><span>지식 ⭐⭐⭐</span><span>전공자이지만 부족한 부분이 많습니다. 많이 노력하겠습니다!</span></div>
                <div><span>깡 ⭐⭐</span><span>MBTI I라서 쑥쓰러움이 많습니다ㅎㅎ..</span></div>
            </div>
        </div>

        <div class="abilities">
            <div class="title">방명록</div>
            <div class="mypostingbox" id="postingbox">

                <div class="form-floating mb-3">
                    <input type="text" class="form-control" id="name" placeholder="이름을 입력해주세요">
                    <label for="name">이름</label>
                </div>
                <div class="form-floating mb-3">
                    <input type="text" class="form-control" id="content" placeholder="내용을 입력해주세요">
                    <label for="content">내용</label>
                </div>

                <div class="mybtn">
                    <button id="postingbtn" type="button" class="btn btn-dark">기록하기</button>
                </div>
            </div>

            <ul class="list-group" id="postinglist">

            </ul>
        </div>

    </div>
</body>

</html>

 

[어떤 문제가 있었는지]

css 속성을 다루는데 어려움이 있었다. 특정 태그를 지정해서 정렬하기, 이 div 태그가 어디까지의 영역이고 그 안에서 어떻게 정렬하고 폰트사이즈는 어떻게 키우는지 어려움이 있었다. 특히 헷갈렸던 건 정렬 부분이었다.

 

[내가 시도해본 것들]

일단 무작정 그 전에 썼던 코드들을 가져다 쓴다던가. html 태그에 id값 붙이고 직접 css 속성 적어가면서 봤었다.

 

[어떻게 해결했는지]

내가 시도해본 부분들이 너무 중구난방이라 이해를 돕기 위해서 gpt 사용을 많이 했다. 

 

[무엇을 새롭게 알았는지]

css 정렬 부분을 정리해보았다.

display: flex;

해당 컴포넌트를 flex 컴포넌트 상태로 하겠다고 명시해주는 코드이다. 이걸 명시 해준 후 다른 flex 속성을 정의해가면서 정렬한다.

 

justify-content: space-between;

자식 요소들을 사이의 수평공간을 어떻게 할지 정의하겠다는 코드이다. space-between은 첫번째는 맨 왼쪽 정렬, 마지막은 맨 오른쪽 정렬, 나머지들은 왼쪽과 오른쪽 사이에 공평하게 나누어서 각자 배치하겠다는 의미이다. 이외에도,

 

  • flex-start: 아이템들을 왼쪽으로 정렬합니다.
  • flex-end: 아이템들을 오른쪽으로 정렬합니다.
  • center: 아이템들을 가운데 정렬합니다.
  • space-around: 아이템들 사이와 양 끝에 균등한 간격을 둡니다.
  • space-evenly: 아이템들 사이와 양 끝에 동일한 간격을 둡니다.
align-items: center;

 

align-items는 자식요소들을 세로방향을 어떻게 정렬할지 정의하는 코드이다. center는 가운데로 정렬하겠다는 의미이고 높이가 다른 요소들도 가운데를 기준으로 정렬한다. 이외에도,

 

  • flex-start: 자식 요소들을 위쪽으로 정렬합니다.
  • flex-end: 자식 요소들을 아래쪽으로 정렬합니다.
  • stretch: 자식 요소들이 컨테이너의 높이에 맞게 늘어납니다. (기본값)
  • baseline: 텍스트의 **기준선(baseline)**에 맞춰 정렬됩니다.
flex: 1;

 

flex 속성은 자식요소의 크기조절 방식을 정의한다. flex 속성은 다음과 같이 이루어져 있다.

 

flex: <flex-grow> <flex-shrink> <flex-basis>;

 

  • flex-grow: 남은 공간을 차지할 수 있는 비율.
  • flex-shrink: 컨테이너가 작아질 때 줄어드는 비율.
  • flex-basis: 아이템의 기본 크기.

 

  • flex-grow: 1; → 남은 공간을 동일한 비율로 차지.
  • flex-shrink: 1; → 공간이 부족할 경우 동일한 비율로 줄어듦.
  • flex-basis: 0; → 아이템의 기본 크기를 0으로 설정.

이 flex 속성은 컨테이너 크기가 변화함에 따라 자식 요소들의 크기가 동적으로 맞춰져서 변화한다.

 

flex-direction: column;

기본적으로 Flexbox는 자식 요소를 가로 방향(row)으로 정렬하지만, flex-direction: column;은 이를 세로 방향(column)으로 정렬하도록 한다.

 

gap: 20px;

 

 

gap은 자식요소들 간 사이에 간격을 추가하는 속성이다.

 

따라서 이 코드를 예시로 설명하자면,

/* Details Section */
        .details {
            display: flex;
            justify-content: space-between;
            gap: 20px;
            margin-bottom: 40px;
        }

flex요소로 배치할것이며, 각 요소들을 수평으로 정렬할때 맨왼쪽은 왼쪽 정렬, 맨 오른쪽은 오른쪽 정렬, 나머지 것들은 공평하게 사이간격을 나눈다. 그리고 요소간 간격은 20px, margin-bottom은 40px으로 div 영역 내에서 아래쪽 margin이 40px만큼 생기게 한다는 의미이다.

 

그리고 강의에서 배운 내용 이외에 추가적을 구현한 내용이 있다. 방명록 기능을 구현할 때 이 방명록을 남긴 시간도 같이 남기고 싶어서 시간을 표시하는 기능도 구현했다. 

// 현재 시간 추가
let now = new Date();
let timestamp = now.toLocaleString(); // 시간 형식 변환 (YYYY-MM-DD HH:mm:ss)

Date()를 이용해 객체를 생성하고 현재 날짜와 시간을 얻는다. 그리고 toLocaleString() 메서드를 사용하여, Date 객체를 사람이 읽기 쉬운 현지 시간 형식으로 변환한다.

 

그리고 데이터베이스 필드에 다음과 같이 추가한다.

let doc = {
                'name': name,
                'content': content,
                'timestamp': timestamp, // 시간 필드 추가
            };

 

방명록 기능

따라서 방명록을 하나씩 남길때마다 남긴 시간도 뜨도록 구현했다. 

 

[내일 할일]

내일은 SQL강의를 집중적으로 듣고 개인페이지 css를 조금 더 수정해야할 것 같다. 아마도 정렬부분과 색상이나 테마를 하나로 잡고 그에 맞게 디자인해야할 것 같다.  그리고 리드미 뼈대에 내용을 추가해야겠다! 오늘도 알찬 하루였다 😊👍