본문 바로가기
슈퍼코딩 부트캠프/week3

2024.07.08(월) 슈퍼코딩 부트캠프 신입연수원 week3 Day1 일일보고

by zldn 2024. 7. 9.

<To-Do List>

- 58~63강 강의듣기

- 일일보고 작성하기

 

<배운 내용 요약 정리>

58~59강 저장된 게시글을 확인하려면? GET!

1. index.js를 통해서 메인화면 구성하기

 

>> 원래 맨 처음에 html에서 화면을 구성했던 코드

<div class="item-list">
  <div class="item-list__img">
    <img src="assets/image.svg" alt="img" />
  </div>
  <div class="item-list__info">
    <div class="item-list__info-title">블루투스 키보드 팝니다</div>
    <div class="item-list__info-meta">서초동 4시간 전</div>
    <div class="item-list__info-price">5,000원</div>
  </div>
</div>;

 

>> html코드를 바탕으로 js에서 다음과 같이 구현할 수 있다.

const renderData = (data) => {
  // data = [{id:1,title:'aa'...},{id:2,title:'bbb'...}..]이런형태
  const main = document.querySelector("main");

  data.reverse().forEach(async (obj) => {
    // 배열 각각에 대해서 반복문 돌리기
    // 리버스해주는 이유는 최근 작성한 것이 위에 올라오도록 하기 위해서 해줌

    const itemListDiv = document.createElement("div");
    itemListDiv.className = "item-list";

    const itemListImgDiv = document.createElement("div");
    itemListImgDiv.className = "item-list__img";

    const img = document.createElement("img");
    const res = await fetch(`/images/${obj.id}`);
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    img.src = url;

    const itemListInfoDiv = document.createElement("div");
    itemListInfoDiv.className = "item-list__info";

    const itemListInfoTitleDiv = document.createElement("div");
    itemListInfoTitleDiv.className = "item-list__info-title";
    itemListInfoTitleDiv.innerText = obj.title;

    const itemListInfoMetaDiv = document.createElement("div");
    itemListInfoMetaDiv.className = "item-list__info-meta";
    itemListInfoMetaDiv.innerText = obj.place + " " + calcTime(obj.insertAt);

    const itemListInfoPriceDiv = document.createElement("div");
    itemListInfoPriceDiv.className = "item-list__info-price";
    itemListInfoPriceDiv.innerText = obj.price;

    itemListImgDiv.appendChild(img);

    itemListInfoDiv.appendChild(itemListInfoTitleDiv);
    itemListInfoDiv.appendChild(itemListInfoMetaDiv);
    itemListInfoDiv.appendChild(itemListInfoPriceDiv);

    itemListDiv.appendChild(itemListImgDiv);
    itemListDiv.appendChild(itemListInfoDiv);

    main.appendChild(itemListDiv);
  });
};

 

 

2. 시간기능 구현하기

- 위에까지 하고나면 우리가 작성했던 css가 있기때문에 위치에 맞춰서 글자정보들은 다 들어가 있을 것이다.

- 이제 calcTime()이라는 함수를 이용하여 시간정보를 구현해보자.

const calcTime = (timestamp) => {
  // 한국시간 = UTC + 9시간
  const curTime = new Date().getTime() - 9 * 60 * 60 * 1000;
  const time = new Date(curTime - timestamp);
  const hour = time.getHours();
  const minute = time.getMinutes();
  const seconds = time.getSeconds();

  if (hour > 0) return `${hour}시간 전`;
  else if (minute > 0) return `${minute}분 전`;
  else if (seconds > 0) return `${second}초 전`;
  else return "방금 전";
};

 

 

3. 이미지 불러오기

 

>> 서버에 get요청을 보내서 이미지 요청한 후 반환받은 값에 대해서 이미지(2진법)으로 변경하기

- 이미지는 우리가 blob(16진수)로 db에 저장을 해두었다.

- 그래서 이 blob를 자바스크립트에서 받아서 이미지로 변경하는 작업이 필요하다.

const img = document.createElement("img");
const res = await fetch(`/images/${obj.id}`);
const blob = await res.blob();
const url = URL.createObjectURL(blob);
img.src = url;

 

>> 서버에서 get요청을 받으면 이미지 정보를 반환하기

 

@app.get("/images/{item_id}")
async def get_image(item_id: int):
    cur=con.cursor()
    image_bytes = cur.execute(f"""
                          SELECT image FROM items WHERE id={item_id}
                          """).fetchone()[0]
    return Response(content=bytes.fromhex(image_bytes),media_type="image/*")

 

 

 

 

+ 커밋방법

1. 메세지로

-> feat: //기능 구현시 작성

-> fix: //버그 수정시 작성

 

2. 이모지로(깃모지)

-> ✨: //기능 구현

->  🐛: //버그 수정

-> 🚀: //배포

 

61강 Big Picture

>> 회원가입과 로그인 과정을 실제로 구현해보면서 살펴보기

1. 회원가입 기능 : 프론트 -> 백엔드 : 회원가입 요청하기 / 백엔드 -> DB : user 정보 저장하기

2. 로그인 기능: 프론트 -> 백엔드 : 로그인 요청하기 / 백엔드 -> DB : 조회 후 로그인되면 Access Token(JWT)을 발급

3. 로그인 유지 기능 -> 서버에 요청을 할때마다 발급받은 Access Token을 header에 넣어서 요청

4. 브라우저가 닫혀도 로그인 유지 기능 -> 로컬 스토리지 이용

62 ~ 63강 프론트엔드 회원 가입 기능 구현하기

1. 회원 가입 요청(id, password) ->  프론트엔드에서 해시를 통해 암호화해서 서버로 데이터를 보낸 후 회원가입 처리하기

>> 해시란?

- 임의의 길이의 입력 값을 고정된 길이의 출력값으로 변환하는 함수

- 동일한 입력에 대해 동일한 출력을 반환

- 프론트엔드에서 해시를 이용해 최소한의 보안장치를 한 후 백엔드로 보내줌(이때 sha-256 이용)

- https://coding.tools/sha256 sha-256에 대해서 일정길이의 문자열을 내보냄을 알 수 있음

 

>> 프론트엔드에 간단히 구현하기

 

- form (input, button)을 이용하여 html 만들기

<form id="signup-form" action="/signup" method="POST">
      <div>회원가입</div>
      <div>
        <label for="id">아이디</label>
        <input type="text" id="id" name="id" required />
      </div>
      <div>
        <label for="password">패스워드</label>
        <input type="password" id="password" name="password" required />
        <!-- pssword타입으로 하면 화면에서는 안보이게 처리됨 -->
      </div>
		...
    </form>

 

- 구현화면

 

html로 작성

 

>> .js를 통해 프론트엔드에서 해시를 통해 암호화하여 백엔드로 보내는 기능 구현

 

- html에서 submit버튼이 눌리면 formData를 받아와서 해시로 암호화하기(handleSubmitForm함수로 구현)

- 이때 패스워드란과 패스워드 확인란의 입력이 동일한지 안한지 checkPassword 함수로 확인

- 동일하다면 암호화한 해시를 서버에 보내고 서버에서 formData를 잘 받으면 "200"을 리턴하고 회원가입 완료메세지 띄우기

- 동일하지 않다면 오류메세지 띄우기

 

*이때 sha-256을 이용하기 위해서는 html의 바디 아래쪽에 다음과 같이 작성하기

 <script src="https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.9.0/sha256.min.js"></script>

 

* checkPassword 함수

const checkPassword = () => {
  const formData = new FormData(form);
  const password1 = formData.get("password");
  const password2 = formData.get("password2");

  if (password1 === password2) {
    return true;
  } else return false;
};

 

*handleSubmitForm 함수

const handleSubmitForm = async (event) => {
  event.preventDefault();
  const formData = new FormData(form);
  const sha256Password = sha256(formData.get("password"));
  //   폼데이터의 password내에 있는 데이터를 받아와서 sha256으로 변환
  formData.set("password", sha256Password);
  //   formData 내에서 우리가 signup.html에서 password라고 name 지었던 것에 sha256Password를 설정하기

  const div = document.querySelector("#info");
  if (checkPassword()) {
    const res = await fetch("/signup", {
      method: "post",
      body: formData,
    });
    const data = await res.json();
    if (data === "200") {
      div.innerText = "회원가입에 성공했습니다!";
      div.style.color = "blue";
    }
  } else {
    div.innerText = "비밀번호가 일치하지 않습니다.";
    div.style.color = "red";
  }
};

 

2. 백엔드 -> DB : 사용자 추가

>> .py 서버에서는 POST 요청을 받으면 아이디와 비밀번호를 입력받아 DB에 저장하는 기능 구현

 

- 우선 dbeaver를 통해 users 테이블 추가해주기

CREATE TABLE IF NOT EXISTS users (
         id TEXT PRIMARY KEY,
         name TEXT NOT NULL,
 	     email TEXT NOT NULL,
 	     password TEXT NOT NULL
);

 

- sql문을 이용하여 DB에 저장하기

@app.post("/signup") # 프론트엔드에서 폼을 통해서 post로 보냄(회원가입을 시켜달라고 요청을 보내는거니까)
def signup(id:Annotated[str,Form()],
           password:Annotated[str,Form()],
           name:Annotated[str,Form()],
           email:Annotated[str,Form()]):
    cur.execute(f"""
                INSERT INTO users(id,name,email,password)
                VALUES ('{id}','{name}','{email}','{password}')
                """)
    con.commit()
    return "200"

 

* 입력받으면 다음과 같이 아이디와 해시로 암호화된 비밀번호가 서버에 들어옴(아이디=a, 비밀번호=123 작성했었음)

 

* 회원가입을 할때마다 다음과 같이 서버에 작성됨(dbeaver 데이터 사진)

회원가입 서버

 

<과제>

 

>> ERD를 이용해서 수강신청에 대해 그리기

* PK(기본키) = 각 데이터를 고유하게 식별할때, 기본 키

* FK(외래키) = 다른 테이블에 있는 기본 키를 참조해서 사용할 때, 외래 키

* 그리는 법

그리는 법

-> 내가 작성한 ERD

수강신청 ERD

<하루를 돌아보며>

>> 오류 및 해결

- 59강에서 서버에서 get 요청을 받은 후 이미지정보를 반환하는 과정에서 아무리 강의와 똑같이해도 내 서버에서는 이미지가 띄워지지 않았다. 그래서 뭐가 문제인가 정말 한참을 고민했는데  item id의 자료형을 명시하지 않아 문제가 된 것임을 알고 수정하여 해결하였다.

- 또한 get요청을 받을때의 인자에서 item.id라고 작성을 했었는데 ' . '을 사용하면 객체로 인식을 해 오류가 생길 수도 있다는 점을 고려하여 item_id라고 수정하기도 했다.