https://gray-behavior-aa7.notion.site/8045c16ceddc4129b2a534ab627e9f6a

 

운영체제 강의 공부 정리 | Notion

kocw 반효경 교수님 - 운영체제 강의 공부 정리

gray-behavior-aa7.notion.site

'CS 공부 > 컴공 기초' 카테고리의 다른 글

객체지향 vs 절차지향 프로그래밍  (0) 2021.01.22
코틀린(Kotlin)  (0) 2021.01.22
프레임워크(Framework)  (0) 2021.01.22

https://gray-behavior-aa7.notion.site/5cd2baa12dd849468989ac4c3e2233a3?pvs=4

 

네트워크 강의 공부 정리 | Notion

널널한 개발자 TV - 네트워크 기본 이론 강의 (38강좌)

gray-behavior-aa7.notion.site

시맨틱 태그와 seo 관련 글에서 seo 성능을 확인하기 위해 크롬의 도구인 lighthouse을 사용할 수 있다는 것을 알았다!

그래서 잠깐 짬을 내서 투두리스트도 시험해봤는데 오! 100 이 떴다 😮😮😀

 

 

원했던 기능

  1. 유저가 스크롤을 내릴 때도 상단에 고정되는 헤더를 만들고 싶었다.
  2. 원래 헤더는 투명으로 하고, 브라우저에서 스크롤을 내리고 있는 동안 헤더가 잘보이도록 헤더의 배경색을 하얀색으로 바꿔주고 싶었다.

 

Window.scrollY를 사용해서 스크롤되고 있는지를 감지

Window.scrollY - Web API | MDN

scrollY는 읽기 전용 속성으로, 문서가 수직으로 얼마나 스크롤됐는지 픽셀 단위로 반환한다.

updateScroll 함수를 만들어서 scrollY가 75 (=헤더의 높이) 이상이면 현재 스크롤되고 있는 것으로 판단해서 scrollFlag를 true로 한다. 여기서 scrollFlag는 useState를 활용한다.

 

HeaderContainer에 isScroll 변수명으로 props를 만들고 scrollFlag를 넣는다.

scrollFlag의 초기값은 false로 하고, 스크롤이 감지되면 true로 업데이트하고 리렌더링 한다.

 

scrollFlag가 false일 땐 배경색을 투명으로 하고, true일 땐 배경색을 흰색으로 하고 밑테두리 css를 추가했다.

또한 상태 전환 시 transition으로 끊김이 없어보이도록 변화를 부드럽게 만들었다.

const [scrollFlag, setScrollFlag] = useState(false);

 const updateScroll = () => {
    const { scrollY } = window;
    const scrolled = scrollY > 75;

    setScrollFlag(scrolled);
  };

<HeaderContainer isScroll={scrollFlag}></HeaderContainer>

const HeaderContainer = styled.header`
  position: fixed;
  width: 100%;
  height: 75px;
  background-color: transparent;
  color: #fff;
  z-index: 9999;
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  align-items: center;

  transition: all 0.8s ease-out;
  ${(props) =>
    props.isScroll &&
    css`
      background-color: #ffffff;
      border-bottom: 1px solid rgba(0, 0, 0, 0.3);
    `};
`;

 

그래서 window.scrollY로 스크롤이 발생할 때마다 state를 업데이트하면 되겠다는 생각까진 도달했다.


 

하지만 scroll 이벤트는 유저가 스크롤을 조금만 움직여도 실행되기 때문에 scroll을 할 때마다 state를 변경하려는 생각은 잠시 접어두자… 해당 컴포넌트가 수십번 불필요하게 리렌더링되기 때문이다.

 

scroll 이벤트를 제어하기 위해서는 일정 시간 걸어두고 그 안에서 이벤트를 한 번만 실행되도록하는 throttle을 적용하는 게 좋겠다. 여러번 발생하는 이벤트를 일정 시간 동안 한 번만 실행시키는 게 적합해 보이기 때문이다.

 

throttle 만들기

const throttle = (callback, delay) => {
    let timer = null;
    return () => {
      if (timer) return;
      timer = setTimeout(() => {
        callback();
        timer = null;
      }, delay);
    };
};
  1. timer를 담을 변수를 정의
  2. 존재 여부에 따라 setTimeout 함수를 실행시킨다.
    1. timer가 없다면 setTimeout이 실행되지 않은 상태이므로, setTimeout 함수를 실행시키며 정해진 시간 후에 callback 함수를 실행시키고 timer를 다시 비워준다
    2. timer가 있다면 그대로 리턴한다.

 

src/components/Header.jsx

import { useEffect, useState } from "react";
import { Link as L, useLocation } from "react-router-dom";
import styled, { css } from "styled-components";
import { logo } from "assets/img";

function Header() {
  let { pathname } = useLocation();
  const [scrollFlag, setScrollFlag] = useState(false);

  const throttle = (callback, delay) => {
    let timer = null;
    return () => {
      if (!timer) {
        timer = setTimeout(() => {
          callback();
          timer = null;
        }, delay);
      }
    };
  };

  const updateScroll = () => {
    const { scrollY } = window;
    const scrolled = scrollY > 75;

    setScrollFlag(scrolled);
  };

  const handleScroll = throttle(updateScroll, 100);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return (
    <HeaderContainer isScroll={scrollFlag}>
      <Link to="/">
        <img src={logo} alt="LOGO" />
      </Link>

      <nav>
        <NavContainer>
          <li>
            <Link to="/intro" open={pathname === "/intro" && true}>
              Intro
            </Link>
          </li>
          <li>
            <Link to="/crime" open={pathname === "/crime" && true}>
              금융 사기
            </Link>
          </li>
          <li>
            <Link to="/prevent" open={pathname === "/prevent" && true}>
              범죄 예방
            </Link>
          </li>
        </NavContainer>
      </nav>
    </HeaderContainer>
  );
}

export default Header;

 

 

https://velog.io/@dami/ReactThrottle%EB%A1%9C-Sticky-Header-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

useEffect란?

컴포넌트의 최상위 레벨에서 useEffect를 호출하여 Effect를 선언한다.

 

`useEffect(setup, dependencies?)`

React로 제어되지 않는 외부 시스템과 동기화를 유지할 수 있다.

  • 첫번째 인자: setup함수는 선택적으로 클린업 함수를 반환할 수도 있다. React는 컴포넌트가 DOM에 추가되면 셋업 합수를 실행한다. 다시 렌더링할 때마다 (클린업 함수가 있는 경우) 먼저 이전 값으로 클린업 함수를 실행한 다음, 새 값으로 setup함수를 실행한다. 컴포넌트가 DOM에서 제거되면, React는 마지막으로 클린업 함수를 실행한다.
  • 두번째 인자: 옵션, 의존성 배열이 들어간다. 의존성을 전혀 지정하지 않으면 컴포넌트를 다시 렌더링할 때마다 Effect가 다시 실행된다.
  • undefined를 반환한다.

setup 코드는

`const $body = document.querySelector("body"); $body.style.overflow = "hidden";`

cleanup 코드는

`return () => ($body.style.overflow = "auto");`

 

css overflow 속성으로 모달이 나오면 setup코드에서 body가 안움직이도록 하고, 제거되면 cleanup 코드에서 body가 다시 나오도록 했다.

 

src/components/Modal/index.jsx

import { useEffect } from "react";
import { useRef } from "react";
import styled from "styled-components";
import useOutsideClick from "hooks/useOutSideClick";
import ModalContainer from "./ModalContainer";

function Modal({ onClose, children }) {
  const modalRef = useRef(null);

  const handleClose = () => {
    onClose?.();
  };

  useEffect(() => {
    const $body = document.querySelector("body");
    $body.style.overflow = "hidden";
    return () => ($body.style.overflow = "auto");
  }, []);

  return (
    <ModalContainer>
      ...
    </ModalContainer>
  );
}

export default Modal;

리액트 프로젝트는 기본적으로 index.js에서 렌더링한다.

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Router>
      **<App />**
    </Router>
  </React.StrictMode>,
);

reactDOM 라이브러리가 제공하는 reactRoot 함수가 반환하는 render 함수를 이용하는데, createRoot 함수는 인자로 먼저 `document.getElementById(”root”)`를 받는다.

여기서 id가 root인 HTML 요소는 `public/index.html`에 있다.

아래 html 파일이 개발서버를 구동했을 때 화면에 보이게 되는 html 파일이라고 볼 수 있다.

 

public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    ...
  </head>
  <body>
    <div id="root"></div>
    **<div id="modal"></div>**
  </body>
</html>

root 페이지와 분리해서 만들기 위해 `id=”modal”`를 index.html에 만들어 뒀다.

 

 

 

CreatePortal 이란?

자식들을 DOM의 다른 부분으로 렌더링해주는 기능을 제공해준다.

createPortal – React

https://xionwcfm.tistory.com/316#google_vignette

 

src/components/Modal/ModalContainer.jsx

import { createPortal } from "react-dom";

function ModalContainer({ children }) {
  return createPortal(<>{children}</>, document.getElementById("modal"));
}

export default ModalContainer;

`createPortal(children, domNode, key?)`

  • 첫번째 인자: domNode의 children이 될 컴포넌트 혹은 jsx가 들어간다
  • 두번째 인자: children의 부모가 될 domNode를 넣어준다. index.html에 `<div id="modal"></div>`를 미리 만들어 뒀다.
  • 세번째 인자: 옵션인 키. 포털의 키로 고유한 문자열이나 숫자가 들어간다.
  • JSX에 포함될 수 있는 것이나 리액트 컴포넌트로 반환될 수 있는 React 노드를 반환한다.
  • 그래서 리액트가 렌더링 output에서 이를 발견하면 domNode안의 children을 출력한다!

 

src/components/Modal/index.jsx

import { useEffect } from "react";
import { useRef } from "react";
import styled from "styled-components";
import useOutsideClick from "hooks/useOutSideClick";
import ModalContainer from "./ModalContainer";

function Modal({ onClose, children }) {
  const modalRef = useRef(null);

  const handleClose = () => {
    onClose?.();
  };

  return (
    <ModalContainer>
      <Overlay>
        <ModalWrap ref={modalRef}>
          <Contents>{children}</Contents>
        </ModalWrap>
      </Overlay>
    </ModalContainer>
  );
}

export default Modal;

 

모달 닫고 끄는 로직은 useState를 이용!

state가 true일 땐 모달을 보여주고, false일 땐 모달을 보여주지 않는다.

 

HelpIcon눈에 띄는 색깔로 모달을 띄워 줄 글자(LRAT)를 강조하고, 사용자가 글자를 클릭하면 모달이 열린다.

→ LratModal.jsx가 랜딩된다

 

src/pages/Intro/MethodArea.jsx

import LratModal from "./LratModal";

function MethodArea() {
  const [isOpen, setIsOpen] = useState(false);

  const onOpen = () => {
    setIsOpen(true);
  };

  const onClose = () => {
    setIsOpen(false);
  };

  return (
    <Container>
      <SubTitle content="데이터 분석 방법" />

      <SubDesc>
        <span onClick={onOpen}>
          <HelpIcon /> LRAT (Lifestyle and Routine Activity Theories)
        </span>
        의 세 가지 요소를 개념화(operationalization)했습니다.
      </SubDesc>

      ...
      {isOpen && <LratModal onClose={onClose} />}
    </Container>
  );
}

export default MethodArea;

 

 

components 폴더에 만들어 둔 Modal을 import해서 모달 내용을 채운다.

‘확인’ 버튼이나 ‘X’ 아이콘을 클릭하면 모달이 닫힌다.

 

src/pages/Intro/LratModal.jsx

import styled from "styled-components";
import { Modal } from "components";
import { modalTree } from "assets/img";
import { MdClose } from "react-icons/md";

function LratModal({ onClose }) {
  return (
    <Modal onClose={onClose}>
      <CloseBtn onClick={onClose} />

      <Content>
        ...
      </Content>

      <Button onClick={onClose}>
        <p align="center">확인</p>
      </Button>
    </Modal>
  );
}

export default LratModal;

모달 도입 이유?

  • 페이지 상 중요 내용은 아니었다. (LRAT 이론을 사용하게 된 배경)
  • 그러나 UX를 생각했을 때, 사용자가 이해하기 어려울 수 있을 것 같았다.
  • 정보가 궁금한 사용자는 글자를 클릭함으로써 → 정보를 보기 쉽게 정리해서 보여주고 → ‘확인’이나 ‘X’ 버튼을 누르면 다시 메인 페이지로 가는 플로우로 짜면 되겠다.
  • 그러면 Modal 을 도입하면 되겠지!

 

모달의 역할?

  • FE 포지션 친구와 얘기하면서 친구가 보통 모달은 알림창이나 입력창으로만 봐서 추가정보도 띄워주는 것인지? 의문을 제기해줬다.
  • 그래서 의문을 해결하기 위해 모달 컴포넌트는 왜 사용하는지, 역할이나 목적은 어떤 게 있는지 검색해봤다.

Modal과 Popup의 차이를 먼저 알아봤다.

두 가지 모두 사용자에게 정보를 보여주거나 상호작용을 유도하는 데 사용된다.

  • Popup은 친구가 보통 봐왔던 광고나 알림 메시지 등 짧은 정보를 표시하는 데 주로 사용된다.
  • Modal은 팝업과 비슷하게 사용자에게 정보를 보여주거나 추가 작업을 유도하는 데 사용하지만, 뷰포트 안에 콘텐츠를 중앙에 위치시켜 모달 외부의 요소에 대한 상호작용이 일시적으로 막히는 특징이 있다. 그래서 추가적인 정보를 제공, 정보를 입력받는 양식으로 주로 사용되며 사용자의 주의를 집중시키는 힘이 팝업보다 강하다.

정리하면,

Popup은 현재 창을 벗어난 새 창으로 나타나지만, Modal은 현재 창인 뷰포트 안에 나타난다.

참고: https://brunch.co.kr/@minakoro/156, https://www.coneboy.kr/54/?idx=18153167&bmode=view

 

검색한 결과를 토대로 친구와 다시 얘기해본 결과,

  • 현재 띄워줄 추가 정보는 Popup이 아니라 Modal로 만들면 알맞을 것 같다는 의견으로 일치되었다.
  • Front-end 개발자는 사용자가 보기 쉽게 화면을 구성하는 것도 중요하지만, 사용하려는 컴포넌트가 내가 의도하는 역할과 상응하는지 검토하는 작업도 필요하다는 것을 깨달았다.

 

모달 사용 시 신경 쓴 점

  • 필요한 경우에만 사용
    • 사용자의 결정으로 필요할 때만 띄워주기
  • 간결하고 명확하게 집중할 수 있게 함
    • 모달 표시 내용은 한 눈에 이해할 수 있도록 디자인하기
    • 모달을 띄웠을 땐 메인 페이지의 스크롤은 막아놓기
  • 쉽게 닫을 수 있어야 함
    • 사용자가 쉽게 닫을 수 있도록 ‘닫기’나 ‘확인’버튼을 명확하게 하기
    • 모달 바깥 배경을 클릭해서 모달을 닫는 옵션도 제공하기

 

개발과정

src/components/Modal

→ 모달은 한 번 만들어 두면 나중에 또 쓰일 것 같아서 공통 컴포넌트 폴더에 생성했다.

 

React에서 기본적으로 제공해주는 createPortal api를 사용

모달을 띄웠을 땐 메인 페이지 body의 스크롤은 막아놓기

모달 바깥 배경을 클릭해서 모달을 닫는 옵션 제공하기

 

CSS 의 ::before및 ::after 가상 요소를 사용하면 HTML에 있을 필요 없이 페이지에 콘텐츠를 삽입할 수 있다. 최종 결과는 실제로 DOM에 없지만 페이지에는 마치 DOM에 있는 것처럼 나타난다.

왜 사용하나?

  • 생성된 콘텐츠가 위치적으로 요소 콘텐츠 앞에 오기를 원한다.
  • 콘텐츠 ::after는 소스 순서에서도 "이후"이므로 자연스럽게 서로 쌓이면 ::before 위에 배치됩니다.

콘텐츠는 여전히 적용된 요소 내에 있다. 

https://css-tricks.com/almanac/selectors/a/after-and-before/

 

 

z-index: -1

CSS ::before나 ::after에 `z-index: -1` 을 준다면, 가상요소의 컨텐츠가 요소의 컨텐츠보다 뒤에 위치하게 된다.

→ figure의 ::after에 `z-index:-1` 을 주지 않는다면 a태그가 뒤로 가려지기 때문에 범죄분석 페이지로 이동할 수 없게 된다.

 

그런데 ::before에서는 `z-index:-1`  없어도 가능하네?

어찌보면 당연한 .. !

-> before는 a 태그의 전에, after는 a 태그의 뒤에 위치하기 때문에 before는 이미 a태그보다 뒤에 위치해 있다.

 

  • 가상 요소의 컨텐츠가 표시될 때 가장 중요한 것은 선택한 요소의 태그 안에 가상요소가 위치해야 한다는 것이다.
  • 가상 요소도 실제 요소처럼 배치 속성을 부여해서 자유롭게 위치를 조절할 수 있다!

 

figure에 `position: relative`를 넣고 :before이나 :after에 `position:absolute`를 하면 figure를 기준으로 자유롭게 위치를 조절할 수 있다.

&:before, &:after {
    content: "";
    position: absolute;
    top: 0px;
    bottom: 0px;
    right: 0px;
    left: 0px;
	z-index: -1;
    border-radius: 50%;
    border: 3px solid #bbb;
  }

 

위처럼 top, bottom, right, left를 모두 0px로 하면, top(위), bottom(아래), right(오른쪽), left(왼쪽)으로부터 0px 떨어져 있도록 하는 것이기 때문에 아래와 같이 요소의 테두리를 감싸고 있는 형태가 된다. (흥미 돋았던 내용이었다)

props를 유연하게 전달하는 방식이므로 자주 사용됨!!

obj의 key와 컴포넌트의 prop이 일치하면, spread operator를 구현할 수 있다.

function App() {
   const props = { name: '철수', age: 37 }
   return (
      <Student {...props} />
   )
}

function Student({name, age}) {
   return (
      <div>
         <h1>{name}<h1>
         <span>{age}</span>
      </div>
   )
}

 

property로 name, age를 갖는 props 객체를 App 컴포넌트 내에서 Student 컴포넌트에 넘겨주고 있다.

 

아래 Student prop을 보면 name, age가 있다. 즉, ...props은 `name: '철수', age: 37`이자

`<Student name: '철수', age: 37 />`인 것이다.

마이페이지, 네트워크, 로그아웃 세개의 메뉴를 우측 끝으로 이동시키고 싶었다..

 

react-bootstrap은 보통 container, Col, Row, Stack 4개의 컴포넌트로 레이아웃을 잡는 편이다.

그런데 Nav 같이 이미 자체적으로 layout이 잡혀져 있는 것도 있다!

  1. px-3
    1. `padding-right`, `padding-left`, Nav 전체적으로 내부 자식에게 간격을 주기 위함
  2. ms-auto
    1. `margin-left: auto` 를 의미함. auto 는 부모의 여백공간만큼을 왼쪽으로 밀어내는 느낌이다, 자주 쓰이는 속성이니 숙지하기!
    2. `Nav.item` 태그 3개를 Nav 태그 하나로 묶어서 덩어리때 내려가게 한다.

 

 

결국 `display: flex;`를 사용했는데 , CSS 레이아웃을 잡을 땐 flex를 많이 사용하므로 알아두는 게 좋다.

flex 를 공부하기 좋은 사이트 → 개구리 꽤나 귀여워  Flexbox Froggy

 

const navigationInformations = [
  { title: '마이페이지', link: '/' },
  { title: '네트워크', link: '/network' },
];


<div
    style={{
    backgroundColor: '#D9DDFF',
    display: 'flex',
    alignItems: 'center',
    padding: '34px 56px',
    }}
>
    <div style={{ fontSize: '2em' }}>MY PORTFOLIO</div>
    <ul
        style={{
            margin: '0 0 0 auto',
            listStyleType: 'none',
            padding: 0,
            display: 'flex',
            gap: 30,
            padding: 0,
        }}
    >
    {navigationInformations.map((navigationItem) => (
        <li
            onClick={() => {
                navigate(navigationItem.link);
        	}}
            style={{
                borderRadius: '10px',
                padding: '6px 14px',
                backgroundColor: '#989CFD',
                color: 'white',
            }}
        	key={navigationItem.title}
        >
        {navigationItem.title}
        </li>
    ))}
    </ul>
</div>

 

'프로젝트 > 엘리스 1차' 카테고리의 다른 글

e.preventDefault() ,e.stopPropagation() ?  (0) 2024.04.18

+ Recent posts