본문 바로가기

내일배움캠프 TIL

내일 배움 캠프 23_08_21 TIL

최종 프로젝트에서 크롤링을 끝마치고 사이드 쪽에 여러가지 인기있는 조합이나 최신순 리뷰글을 필터링해서 보여주는 부분을 우선 구도를 잡고 데이터를 패치해온 부분까지 끝내놓고 팀원들과 공유하는 DB가 완성이 된다면 데이터를 받는 부분을 바꿔끼워주면 우선은 완성되기에 그 부분을 해놓고 오늘은 글을 작성시에 이미지에 태그를 넣기위한 방식을 공부해보았다.

 

이번 최종 프로젝트에서 원하는 정확한 기능으로는 아직 완벽히 짜지 못했지만 입력내용을 입력하고 클릭시에 태그를 넣는데 까지는 성공을 하였다.

 
import React, { useState } from "react";
import { Tag, ImageData } from "./types/Types";
import { supabase } from "./supabase";
import { v4 as uuidv4 } from "uuid";

function ImageTaggerWithDB() {
  const [selectedImage, setSelectedImage] = useState<File | null>(null);
  const [text, setText] = useState("");
  const [tags, setTags] = useState<Tag[]>([]);
  const [images, setImages] = useState<ImageData[]>([]);

  const handleImageClick = (event: React.MouseEvent<HTMLImageElement>) => {
    const rect = event.currentTarget.getBoundingClientRect();
    const x = event.clientX - rect.left;
    const y = event.clientY - rect.top;

    if (text) {
      // 만약 텍스트가 존재한다면 이미지를 클릭 했을 때 x,y 축의 좌표와 text를 저장
      const newTag = { x, y, text: text };
      setTags([...tags, newTag]);
      setText("");
    }
  };
  //이미지파일명이 같으면 supabase Storage에 저장할 수 없기에 랜덤한 파일명으로 바꿔주는 핸들러 코드
  const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.target.files && event.target.files.length > 0) {
      const file = event.target.files[0];
      const originalFileName = file.name;
      const fileExtension = originalFileName.split(".").pop();
      const randomFileName = uuidv4() + "." + fileExtension;
      setSelectedImage(new File([file], randomFileName));
    }
  };

  //저장하기 버튼을 누르면
  const handleSaveImage = async () => {
    const imageUrls: string[] = [];

    //만약 선택된 이미지가 있을 시 supabase Storage에 저장하고 해당 url값을 imageUrls에 저장
    if (selectedImage) {
      const { data, error } = await supabase.storage
        .from("1st")
        .upload(`tags/${selectedImage.name}`, selectedImage);

      if (error) {
        console.error("Error uploading image to Supabase storage:", error);
        alert("이미지 업로드 중 에러가 발생했습니다!");
        return;
      }

      imageUrls.push(data.path);

      const newImage = {
        url: imageUrls,
        tags: [...tags],
      };

      const { error: insertError } = await supabase.from("tag").insert([
        {
          url: imageUrls,
          tags: [...tags],
        },
      ]);

      if (insertError) {
        console.error("Error adding post:", insertError);
        alert("에러가 발생했습니다!");
        return;
      }

      setImages([...images, newImage]);
      setSelectedImage(null);
      setTags([]);
    }
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleImageUpload} />
      {selectedImage && (
        <div>
          <img
            src={URL.createObjectURL(selectedImage)}
            alt="이미지"
            onClick={handleImageClick}
          />
          {tags.map((tag, index) => (
            <div
              key={index}
              style={{
                position: "absolute",
                left: tag.x,
                top: tag.y,
                backgroundColor: "white",
              }}
            >
              {tag.text}
            </div>
          ))}
        </div>
      )}
      <div>
        <input
          type="text"
          value={text}
          onChange={(event) => setText(event.target.value)}
        />
      </div>
      <button onClick={handleSaveImage}>저장하기</button>
    </div>
  );
}

export default ImageTaggerWithDB;

위의 코드인데 텍스트를 입력하고 이미지를 클릭 시 해당 위치에 태그가 들어가게 되고 텍스트도 입력되게 된다

이는 이미지에다가저 클릭핸들러를 할당해 줬기에 이미지에만 지금 태그가 들어 갈 수 있다.

이렇게해서 해당 x,y 값과 텍스트 그리고 이미지를 supabase db에 저장해주었고

 

import React, { useEffect, useState } from "react";
import { supabase } from "./supabase";

const Fetch = () => {
  const [posts, setPosts] = useState<any[]>([]);

  useEffect(() => {
    const fetchPosts = async () => {
      const { data, error } = await supabase.from("tag").select("*");

      if (error) {
        console.error("Error fetching posts:", error);
      } else {
        // Process the URLs and tags before setting the state
        const processedData = data.map((post: any) => {
          const imageUrl = JSON.parse(post.url)[0];
          const tags = JSON.parse(post.tags);
          return { ...post, url: imageUrl, tags };
        });

        setPosts(processedData);
      }
    };

    fetchPosts();
  }, []);

  console.log("posts", posts);

  return (
    <div>
      {posts.map((post: any) => (
        <div key={post.id}>
          {post.url && (
            <div style={{ position: "relative" }}>
              <img
                src={`${process.env.REACT_APP_SUPABASE_STORAGE_URL}${post.url}`}
                alt={`${post.id}`}
              />
              {post.tags.map((tag: any, tagIndex: number) => (
                <div
                  key={tagIndex}
                  style={{
                    position: "absolute",
                    left: tag.x + "px",
                    top: tag.y + "px",
                    backgroundColor: "white",
                  }}
                >
                  {tag.text}
                </div>
              ))}
            </div>
          )}
        </div>
      ))}
    </div>
  );
};

export default Fetch;

이렇게 여기서 가져와서 찍어도 이미지를 따라서 잘 태그가 들어가서 나오는 것까지는 확인을 했다 하지만 타입도 any로 줘놓기는했고 고쳐야할게 아직 많이 남았기에 내일 더 해보아야 겠다.

 

아래는 오늘 한 CS 공부를 저장해두려한다

2 메모리
메모리 계층은 레지스터(CPU), 캐시, 메모리, 저장장치로 구성
-계층 위로 올라갈수록 가격이 비싸지고 용량은 작아지고 속도는 빨라지는 특징이 있음
-계층의 존재 이유는 경제성과 캐시 때문에 존재
--------------------------------------------------------------------
레지스터
-레지스터는 연산에 필요한 데이터를 저장하고 빠른속도로 접근할 수 있는 저장공간
-CPU안에 있는 작은 메모리이다
-휘발성을 띈다
-속도가 가장 빠르다
-기억용량이 가장 적다
--------------------------------------------------------------------
캐시
-캐시 메모리는 컴퓨터가 전원이 꺼지면 지워지지만 제일 빠르게 조회할 수 있는 저장공간이다
-L1,L2 캐시를 지정
  L1: CPU안에 있는 캐시
   L2: CPU 바깥 메모리영역에 있는 캐시
-휘발성을 가진다
-속도가 빠르다
-기억 용량이 적다
-L3 캐시도 존재한다
  L3 : L2보다 성능을 더 높이기 위해 메모리영역에 추가된 캐시
--------------------------------------------------------------------
주기억장치
-주 기억장치는 컴퓨터가 전원이 꺼지면 지워지지만 조금더 빠르게 조회할 수 있는 저장공간이다.
-RAM을 가리킨다
  RAM : 하드디스크로부터 일정량의 데이터를 복사해서 임시 저장하고 이를 필요 시마다 CPU에 빠르게 전달하는 역할
-휘발성을 띈다
-속도는 보통정도
-기억 용량도 보통정도
--------------------------------------------------------------------
보조기억장치
-보조 기억장치는 컴퓨터 전원이 꺼져도 지워지지 않는 저장공간이다.
-HDD, SDD를 가리킨다
-비휘발성을 띈다
-속도가 낮다
-기억 용량이 많다
--------------------------------------------------------------------

캐시란
-데이터를 미리 복사해 놓는 임시 저장소
1. 프로그램이 실행되면 디스크를 읽어서 메인 메모리에 복사해둔다.
2. CPU(MMU)가 메인 메모리에서 데이터를 읽어오며 작업을 처리
3. 이 때 캐시가 중간에서 한번더 메인메모리의 데이터를 복사해두는 것이다.
-빠른 장치와 느린 장치에서 속도 차이에 따른 병목 현상을 줄이기 위한 메모리이다.
-데이터 접근에 오래 걸리는 경우를 해결하고 다시 계산하는 시간을 절약
-즉, 캐시는 계층과 계층 사이에서 속도차이를 해결하기 위한 임시 저장소이다.
    -ex1)레지스터 : 메모리와 CPU 사이의 속도 차이를 해결하기 위한 캐시
    -ex2)주기억장치 : 캐시 메모리와 보조기억장치 사이의 속도 차이를 해결하기 위한 캐시

SRAM은 정적메모리로써 용량은 작지만 속도가 매우 빨라서 캐시 전용 메모리로 많이 쓰인다.
--------------------------------------------------------------------
지역성의 원리
 -지역성 이란 자주 사용되는 데이터의 특성을 의미한다.
 -캐시를 직접 설정할때는 자주 사용되는 데이터를 기반으로 설정해야 하며 이러한 특성을
   지역성 이라고 한다
 
시간 지역성
 -최근 사용한 데이터를 다시 접근하려는 특성
 ex) for문에서 i는 시간 지역성을 띈다

공간 지역성
 -최근 접근한 데이터를 이루고 있는 공간이나 그 가까운 공간에 접근하는 특성
 ex) for문 안에 arr.push(i)가 있다면
        이 arr은 공간 지역성을 띈다(arr 배열 원소에 대한 공간 지역성)

--------------------------------------------------------------------

2-2. 캐시히트와 캐시미스
캐시히트
-캐시에 원하는 데이터를 찾은 것
-위치도 가깝고 CPU 내부버스를 기반으로 작동하여 빠르다
-캐시히트를 하게 되면 해당 데이터를 제어장치를 거쳐 가져오게 된다.
--------------------------------------------------------------------
캐시미스
-해당 데이터가 캐시에 없다면 주 메모리로 가서 데이터를 찾아오는 것
-메모리를 가져올때 시스템 버스를 기반으로 작동하기 때문에 느리다
--------------------------------------------------------------------
캐시 매핑
-캐시가 히트되기 위해 매핑되는 방법
-CPU의 레지스터 와 주 메모리(RAM) 간에 데이터를 주고 받을 때를 기반
-주 메모리에 비해 굉장히 작은 레지스터가 캐시 계층으로써 역할 -> 때문에 매핑이 중요
--------------------------------------------------------------------
직접 매핑
-메모리가 1~100이 있고 캐시가 1~10이 있다면 1:1~10, 2:1~20... 과 같이 매핑
-처리가 빠르지만 충돌 발생이 잦다
--------------------------------------------------------------------
연관 매핑
-순서를 일치하지 않고 관련 있는 캐시와 메모리를 매핑
-충돌이 적지만 모든 블록을 탐색하여 속도가 느리다
--------------------------------------------------------------------
집합 연관 매핑
-직접 매핑과 연관 매핑을 합쳐 놓은 것
-순서는 일치하지만 집합을 둬서 저장하며 블록화되어 있어 검색이 효율적
--------------------------------------------------------------------

2-3 메모리 할당
메모리에 프로그램을 할당할 때는 시작 메모리 위치, 메모리의 할당 크기를 기반으로 할당한다.
-연속 할당과 불연속 할당으로 나뉜다.

연속할당
-메모리에 연속적으로 공간을 할당하는 것을 말한다.
-고정 분할 방식과 가변 분할 방식으로 나뉜다.

고정분할 방식
-메모리를 미리 나누어 관리하는 방식 입니다.
-한계로는 내부 단편화가 발생합니다

가변 분할 방식
- 매 시점에 프로그램의 크기에 맞게 동적으로 메모리를 나눠서 사용하는 방식.
종류
1. 최초적합 : 위에서부터 바로 보이는 공간에 바로 할당
2. 최적적합 : 가장 크기에 맞는 공간부터 채우고 나머지를 할당
3. 최악적합 : 가장 크기가 큰 공간에 부터 채우고 나머지 할당

한계
-내부 단편화 발생 X
-외부 단편화는 발생할 수 있다.


내부 단편화와 외부 단편화
내부 단편화란
-메모리를 나눈 크기보다 프로그램이 작아서 들어가지 못하는 공간이 많이 발생하는 현상
-즉, 들어갈 수 있는 공간보다 프로그램이 작아서 공간이 남아버리는 것

외부 단편화란
-메모리를 나눈 크기보다 프로그램이 커서 들어가지 못하는 공간이 많이 발생하는 현상
-즉, 들어갈 공간보다 들어갈 것이 더 커서 들어가지 못하고 남아버리는 것

적합에 관하여
1.최초적합(First-fit)
- 메모리 내에서 빈 공간 중 가장 먼저 발견되는 첫 번째 공간에 블록을 할당합니다.
- 블록 크기와 공간 크기가 일치하지 않을 경우, 남은 공간을 새로운 빈 공간으로 남깁니다.
- 초기 메모리 관리를 위한 간단한 방법이지만, 외부 조각화가 발생할 수 있습니다.
2.최적적합(Best-fit)
- 메모리 내에서 블록 크기와 가장 근접한 크기를 가진 가장 작은 빈 공간을 찾아 할당합니다.
- 블록 크기와 공간 크기가 일치하지 않을 경우, 남은 공간을 새로운 빈 공간으로 남깁니다.
- 내부 조각화는 줄일 수 있지만, 메모리를 탐색하는 데 시간이 걸리므로 성능 저하가 있을 수 있습니다.
3.최악적합(Worst-fit)
- 메모리 내에서 가장 큰 빈 공간을 찾아 블록을 할당합니다.
- 할당된 블록 크기와 공간 크기가 일치하지 않을 경우, 외부 조각화가 발생합니다.
- 내부 조각화가 적지만, 빈 공간을 찾는 데 시간이 걸리므로 성능 저하가 있을 수 있습니다.
--------------------------------------------------------------------

불연속 할당
-운영체제에서는 여러개의 작업을 효율적으로 수행해야하기 때문에 불연속 할당방법을 사용하는데
이의 단점으로는
-메모리 공간 할당과 해제 시의 오버헤드가 발생할 수 있다(불필요 할당)
-메모리 공간이 분산되어 있기 때문에, 프로세스가 불연속 공간에 할당될 경우 프로세스의
 페이지 교체와 같은 작업이 더 복잡해질 수 있다.(교체 알고리즘의 최적화가 필요하다)
--------------------------------------------------------------------
운영체제에서 불연속 할당을 사용하는 3가지 방법이 있는데
1. 링크드 리스트(Linked List) : 불연속 공간에 프로세스를 할당할 때, 할당된 공간의 주소를 연결
리스트에 저장하는 방식이고, 이 방식은 메모리 할당과 해제가 빠르지만, 공간 낭비가 발생할 수 있다.
2.비트맵(Bitmap) : 메모리 공간의 각 블록을 0 또는 1 로 표시하여 사용 가능한 블록과 사용 중인 블록을
구분하는 방식으로 이 방식은 링크드 리스트보다 효율적인 공간 관리를 제공하지만, 메모리가 큰 경우
비트맵이 매우 커지는 단점이 존재한다.
3.페이지 테이블(Page Table) : 가상 메모리 시스템에서 사용되는 방식으로, 물리적인 주소 공간을
페이지라는 작은 블록으로 나누어 사용한다. 각 프로세스는 자신의 페이지 테이블을 가지며,
페이지 테이블은 물리적인 주소와 가상 주소를 매핑하는 역할을 한다. 이 방식은 링크드 리스트와
비트맵보다 효율적이고, 가상 메모리를 구현하는 데 필요한 기술이다.
--------------------------------------------------------------------
이 중 페이지 테이블이란
-메모리를 동일한 크기로 나누고 프로그램마다 페이지 테이블을 두어 이를 통해 메모리에 프로그램을 할당
-1)페이징, 2)세그멘테이션, 3)페이지드 세그멘테이션 기법이 존재

1)페이징
-동일한 크기의 페이지 단위로 나누어 메모리의 서로 다른 위치에 프로세스를 할당
-빈데이터(홀)의 크기가 균일하지 않은 문제가 없어지지만 주소 변환이 복잡해진다
2)세그멘테이션
-의미 단위인 세그먼트로 나누는 방식
-코드와 데이터 등을 기반으로 나눌 수 있으며, 함수 단위로 나눌 수도 있음을 의미한다.
-공유와 보안 측면에서 좋다.
-빈데이터(홀) 크기가 균일하지 않는 문제가 발생
3)페이지드 세그멘테이션
-공유나 보안은 세그먼트로 나누고
-물리적 메모리는 페이지로 나누는 방식

--------------------------------------------------------------------
페이지 교체 알고리즘
-메모리 내에 저장된 페이지 중에서 어떤 페이지를 교체할지 결정하는 알고리즘 입니다.
-페이지 교체 알고리즘은 물리적인 메모리 공간이 한정되어 있을 때 페이지 부재(page fault)가
발생하는 상황에서 새로운 페이지를 적재하기 위해 기존 페이지 중 어떤 페이지를 제거할지 결정하는 역할을 한다.
--------------------------------------------------------------------
간단한 알고리즘 (무작위, FIFO)
무작위 - 무작위로 페이지를선정하여 스왑 영역으로 보낸다
FIFO - 처음 메모리에 올라온 페이지를 스왑 영역으로 보낸다.
--------------------------------------------------------------------
이론적 알고리즘 (최적)
-미래의 접근 패턴을 보고 대상 페이지를 선정하여 스왑 영역으로 보낸다.
--------------------------------------------------------------------
최적 근접 알고리즘(LRU, LFU, NUR, FIFO 변형)
LRU - 시간적으로 멀리 떨어진 페이지를 스왑 영역으로 보낸다
LFU - 사용 빈도가 적은 페이지를 스왑 영역으로 보낸다.
NUR - 최근에 사용한 적이 없는 페이지를 스왑 영역으로 보낸다.
FIFO 변형 - FIFO 알고리즘을 변형하여 성능을 높인다.
--------------------------------------------------------------------

1)오프라인 알고리즘
-먼미래에 참조되는 페이지와 현재 할당하는 페이지를 바꾸는 알고리즘
-입력 데이터가 모두 주어진 상태에서 실행되는 알고리즘이며 입력 데이터를 모두 가지고 있기에
실행 중에는 새로운 입력이 들어오지 않는다.
-오프라인 알고리즘은 입력 데이터를 한꺼번에 처리할 수 있으므로, 실행 시간과 공간 사용량을 
예측할 수 있으며, 따라서 입력 데이터의 크기에 따라 적절한 실행 시간과 공간을 예약하여 처리할 수 있다.
-하지만, 미래에 사용되는 프로세스를 알 수는 없다.
--------------------------------------------------------------------
2)시간기반 알고리즘
FIFO(Fist In First Out)
- 가장 먼저 온 페이지를 교체 영역에 가장 먼저 놓는 방법이다.
- 캐시 메모리에 새로운 데이터가 들어오면 가장 오래전에 들어온 데이터를 제거하고 새로운 데이터를 추가한다.
-이 방식은 구현이 간단하지만, 오래된 데이터가 최근에 사용된 데이터와 비슷한 경우에 성능이 저하될 수 있다.
-----------------------------------------------------------------------------------------------------------------
LRU(Least Recently Used)
-참조가 가장 오래된 페이지를 교체하는 방법
-LRU 방식은 가장 최근에 사용된 데이터를 먼저 사용할 가능성이 높기 때문에 캐시 히트율을 높일 수 있다.
-그러나 LRU 알고리즘의 구현 방식은 데이터를 저장하는데 추가적인 비용이 들어가게 된다.
-> 오래된 것을 파악하기 위해 각 페이지마다 계수기, 스택을 두어야하는 문제점 발생
--------------------------------------------------------------------
NUR(Not Used Recently)
-일명 clock 알고리즘으로 최근사용여부를 0,1로 표시하여 교체하는 방법이다.
-시계방향으로 돌면서 0을 찾고 0을 찾는 순간 해당 프로세스를 교체, 해당 부분을 1로 바꾸는 알고리즘이다.
NUR vs LRU
-LRU : 데이터를 사용할 때마다 최근 사용 시간을 갱신
-NUR : .사용하지 않는 데이터를 주기적으로 스캔하여 최근 사용 여부를 판단

--------------------------------------------------------------------
빈도기반 알고리즘
LFU(Least Frequently Used)
-가장 참조 횟수가 적은 페이지를 교체 (=많이 사용하지 않은 것을 교체)
-LFU 방식은 사용 빈도가 낮은 데이터를 제거하여 캐시 히트율을 높일 수 있다.
-그러나 LFU 알고리즘은 일부 데이터가 빈번하게 사용되는 경우에는 성능 저하가 발생할 수 있다.

'내일배움캠프 TIL' 카테고리의 다른 글

내일 배움 캠프 23_08_23 TIL  (1) 2023.08.23
내일 배움 캠프 23_08_22 TIL  (0) 2023.08.22
내일 배움 캠프 23_08_18 TIL  (0) 2023.08.18
내일 배움 캠프 23_08_17 TIL  (0) 2023.08.17
내일 배움 캠프 23_08_10 TIL  (0) 2023.08.10