• 모듈 : 특정 기능을 수행하는 코드 집합 (함수)
  • 패키지 : 관련된 모듈을 분류하는 방법
  • 라이브러리
    • 모듈의 집합,
    • 그림을 그리고 싶어서 붓, 물감, 스케치북같은 여러 미술 도구를 가지고 그림, 이때 추적으로 어떤 미술 도구들을 가져오는 것은 나의 자유!
  • 프레임워크
    • 애플리케이션을 개발하는데 사용되는 구조
    • 소프트웨어 개발을 간편하게 만들기 위한 소프트웨어 개발 환경
    • 비지니스 로직에 집중할 수 있음
    • 나는 화가, 주어진 양식대로 그림을 그려야 하고, 이를 벗어나면 안 된다! 이 그림 양식을 따르면 나는 훌륭하고 가치있는 그림을 그릴 수 있다!
  • API
    • 도서 SNS를 만들고 싶은데, 시중에 나온 책들의 정보들을 내가 수집해서 가져오기는 너무 어렵고 복잡함 -> 내가 원하는 책 정보와 갯수를 담은 도서 정보 내역을 작성하고 이를 대신 수행해주는 업체에 부탁한다고 이해하면 됨

 

네트워크 기본 기식

인터넷 : 컴퓨터 네크워크 통신망

ip : 인터넷 프로토콜, 즉 통신 규약, ip주소는 ip에 필요한 고유 주소

인터넷 통신 시에는 지정한 IP 주소에 데이터를 Packet 이라는 단위로 전달

 

TCP

  • 신뢰성 통신이라 생각하면 됨, 키워드는 신뢰! -> 데이터 잘 받았어요!, 패킷 순서보장해요! 
  • 3 Way HandShake 수행, syn보내면 ack로 잘 받았다고 응답하는 과정, ack는 잘받았고 너의 요청을 수락한다는 의미, 그래서 클라이언트는 ack와 함께 데이터를 전송함 -> 이 과정을 통해서 신뢰성을 보장하지만 대신 시간이 오래걸린다 ㅜㅠ

 

UDP

  • TCP랑 반대라고 보면 됨, 비신뢰성 비연결성 하지만!  3Way HandShake가 없기때문에 속도가 빠름
  • 데이터 무결성 검사하는 체크섬(Checksum)을 포함하기 때문에 잘못된 데이터가 전송되지 않도록하긴함
  • 속도빠르니까 스트리밍, 게임에 많이 사용~

PORT

  • 그냥 간단하게 내 컴퓨터 아이피 주소는 딱 하나임 근데 나는 컴터에서 겜도 하고 인터넷도 하고 카톡도 함 그럼 패킷이 겜으로 갈지 카톡으로 갈지를 모름 ->  그 프로세스를 구분하기 위해서 PORT가 존재!
  • 이미 사용되고 있는 포트 (0 ~ 1023) -> 여긴 피해서 사용하기, 80번은 HTTP

크롬 브라우저에 유튜브 URL을 입력했을 때 발생하는 일

1. DNS를 조회해서 해당  IP주소를 응답받음

2. 웹 브라우저에서 구글 서버로 유튜브 검색해줘!라는 내용의 HTTP요청 메시지가 담긴 요청 패킷을 보냄

3. 구글 서버에서 받은 HTTP요청 메시지를 기반으로 응답 HTTP메시지를 만들어 응답

4. 응답 패킷이 도착하면 HTML이 응답으로 오고 브라우저에 그려짐

 

Scale Up, Scale Out

Scale Up : 단일 서버의 하드웨어 사용을 높임, 처리를 더 빠르게 함

Scale Out : 같은 사영의 서버를 여러개 배치, 더 맣은 사용자 요청을 동시 처리 가능

 

Stateful, Stateless

Stateful : 서버는 클라이언트의 상태를 유지(기억)함. -> 그렇기에 같은 서버가 유지되야 함 -> 그렇기에 에러나면 큰일

Stateless : 클라이언트의 상태를 유지하지 않음 -> 같은 서버를 유지할 필요가 없기에 Scale Out 수평 확장성이 높음 -> 근데 로그인처럼 상태를 유지해야하는 경우가 생김. 그래서 그럴땐 뭐다? 쿠키 세션 토큰을 활용하여 극복!

 

Connection, Connectionless

Connection(연결) : 서버는 클라이언트와 계속해서 연결을 함 -> 새로운 연결 과정을 거치지 않기에 속도는 빠르지만 연결을 위한 자원이 많이 소모됨

Connectionless(비연결) : 서버는 클라랑 연결 유지 안 함 -> 자원을 효율적으로 사용하지만 요청이 오면 3Way HandShake 해야 되서 시간 걸림, 게다가 정적 자원 모두 다시 다운하기에 느림~> 그래서 캐싱 혹은 HTTP 지속연결로 해결

 

HTTP 지속연결(Persistent Connections)

하나의 요청에 필요한 요청들이 모두 응답될 때 까지 연결을 유 즉, 정적 자원을 다 가지고 올 때까지 계속 연결 유지함, 비지속 연결은 정적자원에 대해서 하나씩 응답하고 끊고 연결

 

HTTP(HyperText Transfer Protocol)

  • 요청 응답 구조
  • 무상태 (Stateless) -> 클라이언트의 상태를 보존하지 않음, 로그인은 쿠키 토큰 세션으로 해결
  • 비연결 (Connectionless) -> 서버 자원을 효율적, 정적자원은 계속 다시 받고 속도 오래걸림 -> HTTP 지속연결(Persistent Connections)로 문제를 해결

 

HTTP Message 구조

HTTP Message 구조

 

 

HTTP 요청 메세지(Request Message)

  • Start Line
    • HTTP Method(GET ...)
    • path(HTTP Request가 전송되는 대상, Query String), /search?keyword=yena
    • HTTP Version : 1.1
  • Header
    • Host
    • field-name: OWS(띄어쓰기) field-value 구조 -> Content-Type : text/html  
  • Empty Line : 공백 한 줄, 필수값
  • Message Body : 실제 전송하는 데이터가 담겨 있는 부분

 

HTTP 응답 메세지(Response Message)

 

 

 

Method

POST : Message Body를 통해 요청 데이터를 전달, 멱등성(여러 번 실행해도 동일한 결과)을 보장하지 않는다

GET : 서버에 추가적인 데이터 전송을 해야한다면, Message Body가 아닌 Query String(Query Parameter)를 사용

PUT : 리소스 덮어쓰기, 클라이언트 측에서 리소스를 식별하여 URI를 지정, 없으면 생성 있으면 아예 덮어씀

PATCH : 리소스 부분 업데이트

 

HTTP Status Code

  • 1xx (정보)
  • 2xx (성공)
  • 3xx (리다이렉션)

  • 4xx (클라이언트 에러)
  • 5xx (서버 에러)
  •  

 

HTTP API 설계 방법

  1. HTTP API는 설계시 항상 리소스 식별을 기준
  2. URI에 들어갈 리소스는 복수 형태 board → boards
  3. URL에 동사를 사용하지 않는다.
  4. HTTP Method의 역할을 URL에 포함하지 않는다.

Restful API : REST 기반으로 서비스 API를 구현한 것

REST : URI를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE,PATCH 등)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것

 

구현 규칙

  1. 리소스는 명사를 사용
  2. 복수 형태를 사용
  3. 만약, REST만으로 해결하기 어려운 경우라면 동사를 허용
  4. 자원의 계층 관계를 슬래시(/)로 표현
  5. 마지막 문자에는 슬래시(/)가 있으면 안된다.
  6. 하이픈(-)을 사용
  7. 소문자
  8. URI에 파일 확장자를 포함하면 안된다.
  9. CRUD 함수명은 사용하지 않고, HTTP Method를 활용
  10. 정렬, 필터링, 페이징은 신규 API를 만드는것이 아닌 Query Parameter를 사용
GET /users/123     // 특정 사용자 조회

POST /users        // 사용자 생성
{
    "name": "sparta",
    "password": "codingclub"
}

PUT /users/123     // 사용자 정보 수정
{
    "name": "java",
    "password": "spring"
}

DELETE /users/123  // 사용자 삭제

Keep : 이번 프로젝트에서 진행한 과정 중 다음 프로젝트에서도 유지했으면 하는 부분.

  • 모르는 것이 있으면 최선을 다해서 찾아보고 해결한 노력
  • 각자 다양한 기술을 해볼 수 있게 역할을 골고루 배분한 것
  • 서로 맡은 일을 최선을 다해서 한 것

 

Problem

김예나

 

고민했던 점

  • id값을 다른 문서에 새로 만들어서 그 아이디값을 불러와서 회원 문서에 해당 필드를 따로 추가하여 붙이는 방법을 사용했는데 수정을 할 때 계속 내가 생성하여 만든 시퀀스 id로 조회를 해보니 존재하지 않는다는 문제가 발생했다

 

해결방법

  • 찾아보니 파이어베이스는 데이터를 추가할 때 고유한 id값을 자동으로 만들어준다는 사실을 알게 되었다. 나의 생각으로는 id값 필드명의 중복으로 인해서 계속 문제가 생겼던게 아닐까 싶다.

 

이재민

 

고민했던 점

  • 웹을 만들면서 무조건 내가 외우고 알고 있는 코드들을 활용해서 웹페이지를 만들어야한다는 강박같은 생각에 겁을 먹은 상태라서 뭐든 소극적으로 하나씩 입력하고 있는 나를 발견하였음
  • 팀장님과 팀원들의 배려로 간단한 페이지를 맡게 되었음에도 어떻게 해야하는지 감을 못잡음
  • 함수를 사용했는데 파이어베이스 데이터를 사용하고싶어 script type=module로 코드를 변경하자 기존의 함수가 작동을 안함
  • 모르는 부분이 있어도 용어들이 낯설어 어떤 것들을 모르고 있는지 모르는 답답함

 해결방법

  • 강의를 처음부터 3번정도 돌려서 따라하며 복습함
  • 복습하면서 일단 만들면서 부트스트랩과 인터넷 검색 마지막으로 챗지피티의 도움을 받음
  • 튜터님에게 질문을 드렸음 역시나 모든 내용을 이해하지는 못했으나 대충 튜터님께서 하고자하는 의도파악이 되면서 흥미가 생김

 

정의용

 

고민했던 점

  • 비밀번호를 통한 기존에 작성 했던 방명록의 수정/삭제를 구현하던 도중, DB에 기록된 비밀번호를 다시 불러오고, 새롭게 input된 비밀번호와 일치 여부를 대조하는 과정에서 숫자로만 이루어진 비밀번호일 경우, 불일치가 뜨는 문제가 있었습니다.

 해결방법

  • console.log를 통해 <입력된 비빌번호>, <저장된 비밀번호>, <각 비밀번호의 타입>을 확인하여 영어를 섞을 경우 string으로 반환되지만, 숫자로만 이루어질 경우 불러오는 과정에서 number로 반환되는 현상을 발견하였고, passwordInput === String(storedPassword)를 통해 데이터 타입을 확정적으로 변환하여 대조하게 했습니다.

 

유정현

  • 고민했던 점 :메인 페이지 버튼 눌렀을 때 다음 페이지로 넘어갔다가 메인 페이지로 돌아오기 눌렀을 때 제대로 돌아오는 지 안 오는지 고민했다
  • 해결방법 : 파일 경로에 대해서 알게되어 해결

 

Try : 다음 프로젝트를 위해 해야 할 노력

  • 공식 문서를 열심히 보고 미리 사용할 기술에 대해서 충분히 연습하고 공부하는 것
  • 역할 분배를 좀 더 확실하게 해서 모두들 다양하게 협업할 수 있게 할 것
  • 공지를 잘 숙지 할 것

 

Feel : 이번 프로젝트를 통해 느낀 점

  • 예나 : 무엇인가를 구현해보기 전에 충분한 자료조사를 한 후에 구현하는 것이 맞다는 생각이 들었다.
  • 재민 : 미니팀프로젝트라지만 접근이 너무 어려웠으나 반복해서 내용을 접하다보니 거부감이 사라진 것 같고 모르는 것 생겼을 때 튜터님들 찾아뵙게 질문하고 난 후에 완벽히는 아니지만 어떤식으로 접근하면 될 것 같다는 생각이 들면서 약간의 흥미를 찾음
  • 의용 : 코드가 돌아가는 전체적인 알고리즘은 파악했지만, 기초 지식이 부족해서 각 키워드가 해당 알고리즘에서 구체적으로 어떤 기능을 하는지 모르는 점이 너무 많았고, 코드를 작성하는 문법 측면에서도 새롭게 보는 문법이 많아 코드를 파악하는데 어려움이 있었습니다.
  • 정현 : 웹페이지 하나를 봐도 구성되어 있는 코드가 다양하고 많다는 걸 느꼈고 조금 더 많이 노력을 해야겠다고 생각하였다.

URI와 URL의 차이

  • URI (uniform Resource Identifier): 자원에서 위치 뿐만 아니라 자원의 고유한 식별자를 나타내고 있는 것
    • https://kyn1013.tistory.com/user/1  
  • URL (uniform Resource location) : 자원의 위치를 나타내고 있는 것
    • https://kyn1013.tistory.com/user

 

1. 해당 회원의 상세 페이지로 이동하게 하기

  • 해당 회원의 이름을 눌렀을 때 해당 회원의 식별값(id값 또는 이름)으로  쿼리파라미터방식 또는 패스로 이동하게 만들기
  • encodeURIComponent(uriComponent) : 공백이나 특수문자가 들어간 경우에도 안전하게 해석되도록 하기 위해서 사용
let tempHtml = `
            <div class="col">
                <div class="card">
                    <img src="${image}"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h2 class="name">
                        <a href="member_detail.html?name=${encodeURIComponent(name)}">${name}🐥</a>
                        </h2>
                        <p class="introduction">${comment}</p>
                    </div>
                </div>
            </div>
            `

 

2. 해당 회원의 상세 페이지에서 쿼리파라미터를 추출 한 후에 쿼리를 이용하여 해당 회원의 정보 가져오기

  • window.location.search : url의 쿼리 문자열 부분을 반환 (?name=John&id=123)
  • new URLSearchParams() : 쿼리 문자열을 키-값 쌍으로 만듦
  • query(collection(db, "members"), where("id", "==", memberId));  : 해당 조건에 맞는 값 조회하기
try {
            const q = query(collection(db, "members"), where("id", "==", memberId));
            const querySnapshot = await getDocs(q);

            if (!querySnapshot.empty) {
                const memberData = querySnapshot.docs[0].data();
                document.getElementById("memberDetail").innerHTML = `
                        <p><strong>이름:</strong> ${memberData.name}</p>
                        <p><strong>소개:</strong> ${memberData.introduction}</p>
                        <p><strong>지역:</strong> ${memberData.region}</p>
                        <img src="${memberData.image}" alt="${memberData.name}" style="max-width: 200px;">
                    `;
            } else {
                document.getElementById("memberDetail").innerText = "회원 정보를 찾을 수 없습니다.";
            }
        } catch (error) {
            console.error("Error fetching member details: ", error);
            document.getElementById("memberDetail").innerText = "회원 정보를 로드하는 중 오류가 발생했습니다.";
        }

 

여기서 이름을 누르면!

 

해당 회원의 상세 페이지로 이동!

 

'Web' 카테고리의 다른 글

Restful API 설계 방법  (1) 2025.01.22
팀 소개 웹페이지 프로젝트 회고  (3) 2024.12.30
Firebase를 이용하여 이미지 및 데이터 저장하기  (2) 2024.12.26
웹개발 종합반 5주차  (4) 2024.12.16
웹개발 종합반 4주차  (0) 2024.12.16
<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";
        import { getStorage, ref, uploadBytes, getDownloadURL } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-storage.js";

        // Firebase 구성 정보
        const firebaseConfig = {
            apiKey: ,
            authDomain: ,
            projectId: ,
            storageBucket: ,
            messagingSenderId: ,
            appId: 
        };

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

        $("#addBtn").click(async function () {

            // FireStorage에 저장할 이미지
            let file = document.querySelector('#image').files[0];
            let storageRef = ref(storage, 'image/' + file.name);

            try {
                // 1. 이미지 저장
                await uploadBytes(storageRef, file);

                // Firestore에 저장할 내용
                let imageUrl = await getDownloadURL(storageRef);
                let name = $('#name').val();
                let introduction = $('#introduction').val();
                let comment = $('#comment').val();
                let region = $('#region').val();

                let doc = {
                    'image': imageUrl,
                    'name': name,
                    'introduction': introduction,
                    'comment': comment,
                    'region' : region
                };

                // 2. 데이터베이스 저장
                await addDoc(collection(db, "members"), doc);

                alert("회원 추가가 완료되었습니다!");
                location.href = 'main.html';
            } catch (e) {
                console.error("Error adding document: ", e);
            }
        })

 

  • Firebase에서 이미지를 저장하기 위해서는 Storage에 이미지 자체를 저장한다
  • 그 후에, Storage에 저장된 이미지의 url을 getDownloadURL(storageRef);을 사용하여 추출한 후, FireStore데이터베이스에 저장하면 끝!

 

 

고민했던 점

  • 처음에 팀원 추가 로직을 짤 때 먼저 1. 저장할 데이터를 추출한 후 2. 이미지 저장 3. 데이터베이스에 저장 순으로 짜 봤는데, 생각을 해보니 이미지를 먼저 Storage에 저장한 다음에 그게 성공했을 경우, 데이터베이스에 저장할 데이터를 추출하는게 훨씬 효율적인 로직이라는 생각(만약 사진 저장이 실패한 경우라면, 데이터베이스에 저장할 값들을 추출할 필요가 없기 때문에)이 들어서 수정하게 됐다!

참고로 Cloud Storage를 이용하기 위해서는 결제용 아이디를 만들고 등록해야 한다 ㅠㅠ !!

 

'Web' 카테고리의 다른 글

팀 소개 웹페이지 프로젝트 회고  (3) 2024.12.30
Firebase로 회원 상세 페이지 만들기  (2) 2024.12.28
웹개발 종합반 5주차  (4) 2024.12.16
웹개발 종합반 4주차  (0) 2024.12.16
HTML, CSS를 활용하여 화면 만들기  (3) 2024.12.14

파이썬 크롤링

import requests
from bs4 import BeautifulSoup

URL = "https://bit.ly/web-movie"
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(URL, headers=headers)

soup = BeautifulSoup(data.content, 'html.parser')
title = soup.select_one('#boxoffice_list_content > ul > li:nth-child(1) > a > div.mov_name')

print(title.text) #파묘

 

firebase 연동한 맛집 사이트

 

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

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>푸드파이터</title>
    <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>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Gowun+Dodum&display=swap');

        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        body {
            background-color: white;
            color: black;
        }

        .header {
            background-size: cover;
            background-image:
                url('https://images.unsplash.com/photo-1531697218799-ed0ae884c6c8?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2080&q=80');
            background-position: right;
            height: 650px;
            display: flex;
            flex-direction: column;
        }

        .header>h1 {
            margin: 0;
            font-size: 40px;
        }

        .header>div {
            font-size: 18px;
            margin-top: 10px;
        }

        .form-button {
            width: 150px;
            height: 40px;
            background-color: transparent;
            border: 1px solid tr;
            color: black;
            font-size: 15px;
            margin: 20px 10px 0px 0px;
        }

        .form-button:hover {
            border: 2px solid black;
        }

        .info-button {
            margin: 20px 0 0 15px;
            height: 40px;
            font-size: 14px;
        }

        .post {
            width: 500px;
            margin: 20px 0px 1px 20px;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px transparent;
            background-color: wheat;
        }

        .form-floating input,
        .form-floating textarea {
            color: black;
            background-color: white;
        }

        .button2 {
            display: flex;
            justify-content: flex-end;
            margin-top: 15px;
        }

        .button2>button {
            margin-right: 10px;
        }

        .mycards {
            width: 1600px;
            margin: 30px auto;

            flex-direction: row;
            align-items: center;
        }

        .card {
            border-radius: 30px;
            background-color: white;
            border: none;
            color: black;
            margin-left: 50px;
        }

        .card-img-top {
            object-fit: cover;
            height: 250px;
            border-radius: 20px;
        }

        .card-title {
            margin-top: 10px;
            font-size: 18px;
        }

        .card-body {
            border: blanchedalmond 2px solid;
            border-radius: 20px;
        }

        .card-text {
            color: black;
        }

        .comment {
            color: black;
        }

        .play-button {
            display: flex;
            justify-content: flex-start;
            margin-top: 15px;
        }

        a.nav-link {
            color: #F17228;
            font-size: large;
        }

        .icon {
            height: 50px;
        }

        .card-button {
            background-color: orange;
            color: white;
            text-align: center;
            padding: 10px 15px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
        }

        .card-title {
            font-weight: bold;
        }

        .card-button> :hover {
            background-color: darkorange;
        }

        #input-card {
            width: 500px;
            margin: 0px 0px 0px 135px;
            padding: 20px;
            background-color: #f9f9f9;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            float: left;
        }

        .form-floating input,
        .form-floating textarea {
            color: black;
            background-color: #f9f9f9;
            border: 1px solid #ccc;
            border-radius: 5px;
            margin-bottom: 10px;
            padding: 10px;
            width: 100%;
        }

        .form-floating label {
            color: #333;
        }

        .input-group button,
        .input-group select {
            background-color: rgb(168, 161, 161);
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            margin-top: 10px;
        }

        .input-group button:hover,
        .input-group select:hover {
            background-color: gray;
        }

        .button2 {
            text-align: right;
        }

        .button2 button {
            background-color: #F17228;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }

        .button2 button:hover {
            background-color: #f3620f;
        }

        .jumbotron-message {
            margin-left: 150px;
            font-size: 1500px;
        }
    </style>
    <script type="module">
        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";

        const firebaseConfig = {
            apiKey: "AIzaSyCUiCQL_bvaGqMfdlVekWlwmBjtzAzLAb4",
            authDomain: "sparta-afff6.firebaseapp.com",
            projectId: "sparta-afff6",
            storageBucket: "sparta-afff6.firebasestorage.app",
            messagingSenderId: "483366130794",
            appId: "1:483366130794:web:7285911733b1357631e613",
            measurementId: "G-0DTXP9FSHR"
        };

        const app = initializeApp(firebaseConfig);
        const db = getFirestore(app);

        // 데이터 추가
        $("#addBtn").click(async function () {

            // title_input, comment_input, image_input id를 가진 HTML 요소에서 값을 가져와서 title, comment, image 변수에 저장해 주세요.
            let title = $('#food').val();
            let comment = $('#comment').val();
            let image = $('#image').val();
            let star = $('#star').val();

            try {
                const docRef = await addDoc(collection(db, "foods"), {
                    'title' : title, 
                    'comment' : comment,
                    'image' : image,
                    'star' : star
                });

                alert("음식이 추가 되었습니다!");
                window.location.reload();
            } catch (e) {
                console.error("Error adding document: ", e);
            }
        });

        // 데이터 읽기 및 카드 생성
        $(".row-cols-3").empty();
        const querySnapshot = await getDocs(collection(db, "foods"));

        querySnapshot.forEach((doc) => {

            let title = doc.data().title;
            let comment = doc.data().comment;
            let star = "⭐".repeat(doc.data().star);
            let image = doc.data().image;

            // 문서의 title, comment, image, star 필드에서 데이터를 추출한 변수명을 갖고,
            // tempHtml 문자열에 각 데이터를 포함한 카드의 HTML 코드를 생성하세요.
            let tempHtml = `
            <div class="col">
                <div class="card h-100">
                    <img src="${image}"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h4 class="card-title">${title}</h4>
                        <p class="card-text">${comment}</p>
                        <p>${star}</p>
                        <button class="card-button">주문하기</button>
                    </div>
                </div>
            </div>
            `

            $(".row-cols-3").append(tempHtml);
        });

    </script>
</head>

<body>
    <!-- Navbar -->
    <header class="p-3 text-bg-dark">
        <div class="container">
            <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
                <a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
                    <svg class="bi me-2" width="40" height="32" role="img" aria-label="Bootstrap">
                        <use xlink:href="#bootstrap"></use>
                    </svg>
                </a>
                <ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
                    <img src="https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/food.png"
                        class="icon"></li>
                    <li><a href="#" class="nav-link px-2 text-danger">Foodcourt</a></li>
                    <li><a href="#" class="nav-link px-2 ">홈</a></li>
                    <li><a href="#" class="nav-link px-2 ">한식</a></li>
                    <li><a href="#" class="nav-link px-2 ">일식</a></li>
                    <li><a href="#" class="nav-link px-2 ">중식</a></li>
                    <li><a href="#" class="nav-link px-2 ">양식</a></li>
                </ul>
                <form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
                    <input type="search" class="form-control form-control-dark text-bg-dark" placeholder="Search..."
                        aria-label="Search">
                </form>
                <div class="text-end">
                    <button type="button" class="btn btn-warning me-2">Login</button>
                    <button type="button" class="btn btn-warning">Sign-up</button>
                </div>
            </div>
        </div>
    </header>

    <!-- 점보 트론 적용 jumbotron -->
    <div class="header">
        <div class="p-5 mb-4 bg-body-tertiary rounded-3">
            <div class="container-fluid py-5">
                <div class="jumbotron-message">
                    <h1 class="display-5 fw-bold" style="font-family: 'Black Han Sans', sans-serif; ">스파르타 푸드파이터
                    </h1>
                    <p class="col-md-8 fs-4"> 본인만의 맛집을 소개하는 사이트입니다.
                        <br>맛집을 소개해 주세요!
                    </p>
                </div>

                <!-- 부트스트랩 인풋 박스 적용-->
                <div class="post" id="input-card">
                    <div class="form-floating mb-3">
                        <input type="email" class="form-control" id="image" placeholder="name@example.com">
                        <label for="floatingInput">음식 이미지 주소</label>
                    </div>
                    <div class="form-floating mb-3">
                        <input type="text" class="form-control" id="food" placeholder="영화 제목">
                        <label for="foodTitle">음식명</label>
                    </div>

                    <div class="input-group mb-3">
                        <button class="btn btn-outline-secondary" type="button">별점</button>
                        <select class="form-select" id="star"
                            aria-label="Example select with button addon">
                            <option selected>별점 선택</option>
                            <option value="1">⭐</option>
                            <option value="2">⭐⭐</option>
                            <option value="3">⭐⭐⭐</option>
                            <option value="4">⭐⭐⭐⭐</option>
                            <option value="5">⭐⭐⭐⭐⭐</option>
                        </select>
                    </div>
                    <div class="form-floating">
                        <textarea class="form-control" placeholder="Leave a comment here"
                            id="comment"></textarea>
                        <label for="floatingTextarea">추천 이유</label>
                    </div>
                    <div class="button2">
                        <button type="button" class="btn btn-danger" id="addBtn"> 기록하기 </button>
                    </div>
                </div>
            </div>
        </div>
    </div>


    <!-- 부트스트랩 카드 적용-->
    <div class="mycards">
        <div class="row row-cols-3 row-cols-md-3">
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1565299624946-b28f40a0ae38?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2162&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h4 class="card-title">하와이안 피자</h4>
                        <p class="card-text">이건 꼭 먹어봐야 해요</p>
                        <p>⭐⭐⭐⭐</p>
                        <button class="card-button">주문하기</button>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1481070555726-e2fe8357725c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2235&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h4 class="card-title">크리스피 버거</h4>
                        <p class="card-text">너무 달콤해요!</p>
                        <p>⭐⭐⭐</p>
                        <button class="card-button">주문하기</button>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1569718212165-3a8278d5f624?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2080&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h4 class="card-title">해물 라면</h4>
                        <p class="card-text">국물이 끝내줘요!</p>
                        <p>⭐⭐⭐⭐⭐</p>
                        <button class="card-button">주문하기</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
    </div>

    <div class="container2">
        <footer class="row row-cols-1 row-cols-sm-2 row-cols-md-5 py-5 my-5 border-top">
            <div class="col mb-3">
                <a href="/" class="d-flex align-items-center mb-3 link-body-emphasis text-decoration-none">
                </a>
                <p class="text-body-secondary">©Teamsparta 2024</p>
            </div>
            <div class="col mb-3">
            </div>
            <div class="col mb-3">
                <h5>Section</h5>
                <ul class="nav flex-column">
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Home</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Features</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Pricing</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">FAQs</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">About</a></li>
                </ul>
            </div>

            <div class="col mb-3">
                <h5>Section</h5>
                <ul class="nav flex-column">
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Home</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Features</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Pricing</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">FAQs</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">About</a></li>
                </ul>
            </div>

            <div class="col mb-3">
                <h5>Section</h5>
                <ul class="nav flex-column">
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Home</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Features</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Pricing</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">FAQs</a></li>
                    <li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">About</a></li>
                </ul>
            </div>
        </footer>
    </div>
</body>

</html>

DataBase

  • 데이터를 쉽게 찾고 업데이트 하기 위해서 데이터를 저장하고 관리하는 데이터의 모음
  • 관계형 DB(sql) : 정리된 정보를 다룰 때 사용하는 데이터베이스, (A라는 엔티티는 사람정보만 저장)
  • 비관계형 DB(nosql) : 복잡하거나 유연한 정보를 다룰 때 사용하는 데이터베이스, (A라는 엔티티에 사람, 동물, 식물 정보 모두 저장)

firebase?

  • 구글이 개발한 개발 플랫폼

friestore database?

  • 구글 클라우드를 기반으로 한, nosql 데이터베이스
  • firebase 연동 코드
<script type="module"> //type="module" 로 바꾸는 순간 script파일은 가장 마지막에 호출됨, 태그 안에 onclick같은 태그를 넣어도 실행 안됨
        // 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";


        // Firebase 구성 정보 설정
        // For Firebase JS SDK v7.20.0 and later, measurementId is optional
        const firebaseConfig = {
            apiKey: "AIzaSyCUiCQL_bvaGqMfdlVekWlwmBjtzAzLAb4",
            authDomain: "sparta-afff6.firebaseapp.com",
            projectId: "sparta-afff6",
            storageBucket: "sparta-afff6.firebasestorage.app",
            messagingSenderId: "483366130794",
            appId: "1:483366130794:web:7285911733b1357631e613",
            measurementId: "G-0DTXP9FSHR"
        };


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

 

  • firebase를 이용하여 create
$("#postingbtn").click(async function () {
            let image = $('#image').val();
            let title = $('#title').val();
            let content = $('#content').val();
            let date = $('#date').val();

            let doc = {
                'image': image,
                'title': title,
                'content': content,
                'date': date
            };

            await addDoc(collection(db, "albums"), doc);
            alert('저장 완료!');
            window.location.reload();
        })

 

  • firebase를 이용하여 read
let docs = await getDocs(collection(db, "albums"));
        docs.forEach((doc) => {
            let row = doc.data();

            let image = row['image'];
            let title = row['title'];
            let content = row['content'];
            let date = row['date'];

            let temp_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">${content}</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">${date}</small>
                    </div>
                </div>
            </div>`;
            $('#card').append(temp_html)
        });

 

  • type="module" 로 바꾸는 순간 script파일은 가장 마지막에 호출됨
    • 그렇기에 태그 안에 onclick같은 태그를 넣어도 실행 안되기 때문에 jquery로 직접 이벤트를 넣어줘야 함
    • 또한 결국 마지막에 script파일이 호출되기 때문에 $(document).ready는 호출할 필요가 없음, 그냥 파일안에 실행할 함수를 넣어주면 됨
$(document).ready(function () {
            let url = "http://spartacodingclub.shop/sparta_api/seoulair";
            fetch(url).then(res => res.json()).then(data => {
                let mise = data['RealtimeCityAir']['row'][0]['IDEX_NM'];
                $('#msg').text(mise)
            })
        })

-> 이렇게 할 필요 x

 

let url = "http://spartacodingclub.shop/sparta_api/seoulair";
        fetch(url).then(res => res.json()).then(data => {
            let mise = data['RealtimeCityAir']['row'][0]['IDEX_NM'];
            $('#msg').text(mise)
        })

-> 그냥 이렇게 넣어주면 됨

 

firestore를 적용한 추억앨범

 

코드

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>나만의 추억앨범</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">

    <style>
        @import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap');

        * {
            font-family: "Nanum Pen Script", cursive;
        }

        .mytitle {
            height: 250px;
            background-color: green;
            color: white;

            /*박스 안에 있는 내용물을 정렬할 때 세트로 가는 애들, column은 세로정렬, row는 가로정렬*/
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;

            background-image: url('https://images.unsplash.com/photo-1511992243105-2992b3fd0410?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80');
            background-position: center;
            background-size: cover;
        }

        .mytitle>button {
            width: 150px;
            height: 50px;
            background-color: transparent;
            color: white;
            border: 1px solid white;
            border-radius: 5px;
            margin-top: 20px;
        }

        .mycards {
            width: 1200px;
            margin: 30px auto 0px auto;
        }

        .mypostingbox {
            width: 500px;
            margin: 30px auto 0px auto;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px blue;
            border-radius: 5px;
        }

        .mybtn {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;
        }

        .mybtn>button {
            margin-right: 5px;
        }
    </style>

    <script type="module"> //type="module" 로 바꾸는 순간 script파일은 가장 마지막에 호출됨, 태그 안에 onclick같은 태그를 넣어도 실행 안됨
        // 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";


        // Firebase 구성 정보 설정
        // For Firebase JS SDK v7.20.0 and later, measurementId is optional
        const firebaseConfig = {
            apiKey: "AIzaSyCUiCQL_bvaGqMfdlVekWlwmBjtzAzLAb4",
            authDomain: "sparta-afff6.firebaseapp.com",
            projectId: "sparta-afff6",
            storageBucket: "sparta-afff6.firebasestorage.app",
            messagingSenderId: "483366130794",
            appId: "1:483366130794:web:7285911733b1357631e613",
            measurementId: "G-0DTXP9FSHR"
        };


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

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

            let doc = {
                'image': image,
                'title': title,
                'content': content,
                'date': date
            };

            await addDoc(collection(db, "albums"), doc);
            alert('저장 완료!');
            window.location.reload();
        })

        $("#savebtn").click(async function () {
            $('#postingbox').toggle();
        })

        let url = "http://spartacodingclub.shop/sparta_api/seoulair";
        fetch(url).then(res => res.json()).then(data => {
            let mise = data['RealtimeCityAir']['row'][0]['IDEX_NM'];
            $('#msg').text(mise)
        })

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

            let image = row['image'];
            let title = row['title'];
            let content = row['content'];
            let date = row['date'];

            let temp_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">${content}</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">${date}</small>
                    </div>
                </div>
            </div>`;
            $('#card').append(temp_html)
        });

    </script>
</head>

<body>
    <div class="mytitle">
        <h1>나만의 추억앨범</h1>
        <p>현재 서울의 미세먼지 : <span id="msg">나쁨</span></p>
        <button id="savebtn">추억 저장하기</button>
    </div>
    <div class="mypostingbox" id="postingbox">
        <div class="form-floating mb-3">
            <input type="email" class="form-control" id="image" placeholder="앨범 이미지">
            <label for="floatingInput">앨범 이미지</label>
        </div>
        <div class="form-floating mb-3">
            <input type="email" class="form-control" id="title" placeholder="앨범 제목">
            <label for="floatingInput">앨범 제목</label>
        </div>
        <div class="form-floating mb-3">
            <input type="email" class="form-control" id="content" placeholder="앨범 내용">
            <label for="floatingInput">앨범 내용</label>
        </div>
        <div class="form-floating mb-3">
            <input type="email" class="form-control" id="date" placeholder="앨범 날짜">
            <label for="floatingInput">앨범 날짜</label>
        </div>
        <div class="mybtn">
            <button id="postingbtn" type="button" class="btn btn-dark">기록하기</button>
            <button type="button" class="btn btn-outline-dark">닫기</button>
        </div>
    </div>
    <div class="mycards">
        <div id="card" class="row row-cols-1 row-cols-md-4 g-4">
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1446768500601-ac47e5ec3719?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1446&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">앨범 제목</h5>
                        <p class="card-text">앨범 내용</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">앨범 날짜</small>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1446768500601-ac47e5ec3719?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1446&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">앨범 제목</h5>
                        <p class="card-text">앨범 내용</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">앨범 날짜</small>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1446768500601-ac47e5ec3719?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1446&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">앨범 제목</h5>
                        <p class="card-text">앨범 내용</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">앨범 날짜</small>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://images.unsplash.com/photo-1446768500601-ac47e5ec3719?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1446&q=80"
                        class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">앨범 제목</h5>
                        <p class="card-text">앨범 내용</p>
                    </div>
                    <div class="card-footer">
                        <small class="text-body-secondary">앨범 날짜</small>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

'Web' 카테고리의 다른 글

Firebase를 이용하여 이미지 및 데이터 저장하기  (2) 2024.12.26
웹개발 종합반 5주차  (4) 2024.12.16
HTML, CSS를 활용하여 화면 만들기  (3) 2024.12.14
웹개발 종합반 3주차  (2) 2024.12.13
웹개발 종합반 2주차  (1) 2024.12.12

+ Recent posts