TIL_2024_01_19

이종현·2024년 1월 19일
0

Today_I_Learned

목록 보기
123/145
post-thumbnail

Today 요약

  1. 웹/HTTP/네트워크 책 읽기
  2. 프로젝트

1. What I Learned?

웹/HTTP/네트워크 책 읽기

HTTP의 특징과 데이터 저장 방식

HTTP의 통신과정에서 클라이언트와 서버의 상황에 따라 데이터를 어디에 보관하고 있는지를 고려해야 한다.

모든 정보를 서버에서 관리하는 경우에는 사용자가 많아질 경우, 이에 대한 부하가 서버에 고스란히 전해지기 때문에 클라이언트에서 해당 정보를 관리할 수 있다면 관리하는 것이 서버의 부하를 줄이고 보다 원활하게 통신이 이루어지게 할 수 있다.

클라이언트에 정보를 저장하는 방식에는 쿠키와 웹 스토리지 방식이 있다. 쿠키는 클라이언트와 서버모두 접근이 가능하며 보통 HTTP 헤더에 쿠키 정보가 담겨서 통신된다. 그렇기 때문에 통신하는 속도는 조금 느릴 수 있다. 쿠키의 경우 세션쿠키와 영구쿠키가 있는데, 세션 쿠키는 브라우저가 종료되면 쿠키도 삭제되지만 영구 쿠키는 만료기간이 될때까지 살아남아있는 쿠키다.

웹 스토리지 방식에는 로컬 스토리지와 세션 스토리지가 있다. 로컬 스토리지의 경우에는 브라우저가 종료되도 특별한 동작을 하기 전까지는 계속 스토리지에 남아있고, 세션 스토리지는 브라우저가 종료될 때 스토리지의 정보도 사라진다. 웹 스토리지 방식은 쿠키가 약 4kb의 정보만 저장할 수 있었던 것에 비해 10MB까지 저장이 가능하기 때문에 쿠키에 저장하는 것보다 좀 더 용량이 큰 정보를 저장한다면 웹 스토리지 방식을 사용해야 할 것이다.

그리고 IndexedDB 라는 것이 있는데, 이는 웹 스토리지 방식보다 좀 더 복잡하고 큰 용량의 데이터를 저장하는 데 사용한다.

하지만 이는 모두 클라이언트에서 정보를 저장하는 방식이기 때문에 악의적으로 탈취될 가능성이 있다. 그렇기 때문에 로그인과 관련된 정보와 같은 것들은 해당 방식을 사용하지 않는 것이 좋다. 그렇다면 로그인과 관련된 정보들을 저장해서 활용할 수는 없는 걸까?

이를 해결한 방식이 쿠키와 세션을 활용한 방식이다. 서버에서 권한을 부여할 상황이 생기면 세션ID를 발급해서 클라이언트로 보내고 이를 쿠키에 저장한다. 그럼 클라이언트에서는 요청을 보낼 때 쿠키에 저장된 세션ID를 같이 보내서 인증된 사용자인지를 서버에서 검증할 수 있도록 한다.

이 방법을 사용한다면 클라이언트 저장소에 민감한 정보를 두고 있지 않아도 되며, 서버에서 이런 정보를 두고 관리하기 때문에 보안에 더 유리하다. 하지만 요청이 많아지면 당연히 서버에서 세션 정보를 관리하기 때문에 부하가 늘어날 수 있고, 이는 통신 속도가 느려질 수 있는 원인이 된다.

2. What I did?

프로젝트

useInput 커스텀 훅

// Register 컴포넌트
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [name, setName] = useState('')
const [passwordCheck, setPasswordCheck] = useState('')
const [phone, setPhone] = useState('')
const [emailIsValid, setEmailIsValid] = useState(false)
const [passwordIsValid, setPasswordIsValid] = useState(false)
const [passwordCheckIsValid, setPasswordCheckIsValid] = useState(false)
const [nameIsValid, setNameIsValid] = useState(false)
const [phoneIsValid, setPhoneIsValid] = useState(false)
const [isEmailEmpty, setIsEmailEmpty] = useState(false)
const [isPasswordEmpty, setIsPasswordEmpty] = useState(false)
const [isPasswordCheckEmpty, setIsPasswordCheckEmpty] = useState(false)
const [isNameEmpty, setIsNameEmpty] = useState(false)
const [isPhoneEmpty, setIsPhoneEmpty] = useState(false)
const [isPasswordTouched, setIsPasswordTouched] = useState(false)
const [isPasswordCheckTouched, setIsPasswordCheckTouched] = useState(false)

그냥 단순하게 상태관리하는 것들과 input에 개별적으로 적용되는 handler 함수들..

// Register 컴포넌트
const handleEmailChange = (e: ChangeEvent<HTMLInputElement>) => {
  const { value } = e.target
  setEmail(value)
  setIsEmailEmpty(false)
}

const handleBlurEmail = () => {
  if (email.trim() === '') {
    setIsEmailEmpty(true)
  }

  if (email.length === 0 || isEmailCheck(email)) {
    setEmailIsValid(false)
  } else {
    setEmailIsValid(true)
  }
}

const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
  setPassword(e.target.value)
}

const handleBlurPassword = () => {
  if (password.trim() === '') {
    setIsPasswordEmpty(true)
  } else {
    setIsPasswordEmpty(false)
  }

  if (isPasswordValid(password, email)) {
    setPasswordIsValid(false)
    setIsPasswordEmpty(false)
  } else {
    setPasswordIsValid(true)
  }
}

const handlePasswordTouch = () => {
  setIsPasswordTouched(true)
}

이런 중복되는 로직들을 리팩토링하려고 시도해봤습니다.

아래는 리팩토링의 결과물로 최종적 구현을 완료한 useInput 커스텀 훅입니다.

import { ChangeEvent, useState } from 'react'

interface InputProps {
  initialValue: string
  validator: (val1: string, val2?: string) => boolean
  type: string
  additionValue?: string
}

const useInput = ({
  initialValue,
  validator,
  type,
  additionValue
}: InputProps) => {
  const [value, setValue] = useState(initialValue)
  const [isValid, setIsValid] = useState(false)
  const [isEmpty, setIsEmpty] = useState(false)
  const [isTouched, setIsTouched] = useState(false)

  const validateInput = (inputValue: string) => {
    setIsEmpty(inputValue.trim() === '')

    const validationCondition =
      type === 'email' ||
      type === 'name' ||
      type === 'phone' ||
      type === 'password'

    setIsValid(
      validationCondition
        ? !validator(inputValue)
        : !validator(inputValue, additionValue)
    )
  }

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const { value } = e.target
    setValue(value)
  }

  const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
    const { value } = e.target
    setIsTouched(false)
    validateInput(value)

    if (type === 'passwordCheck') {
      if (!isValid) setIsTouched(false)
      else {
        setIsTouched(true)
      }
    }
  }

  const handleTouch = () => {
    setIsTouched(true)
  }

  return {
    value,
    handleChange,
    handleBlur,
    handleTouch,
    isValid,
    isEmpty,
    isTouched
  }
}

export default useInput

Register 컴포넌트에서 사용합니다

// Register 컴포넌트
const emailInput = useInput({
  initialValue: '',
  validator: isEmailCheck,
  type: 'email'
})
const passwordInput = useInput({
  initialValue: '',
  validator: isPasswordValid,
  type: 'password',
  additionValue: emailInput.value
})
const passwordCheckInput = useInput({
  initialValue: '',
  validator: isPasswordCheckValid,
  type: 'passwordCheck',
  additionValue: passwordInput.value
})
const nameInput = useInput({
  initialValue: '',
  validator: isNameCheck,
  type: 'name'
})
const phoneInput = useInput({
  initialValue: '',
  validator: isPhoneCheck,
  type: 'phone'
})

커스텀 훅으로 변경해서 관리함으로써 좀 더 직관적으로 코드를 이해할 수 있게 된 것 같습니다.

profile
데이터리터러시를 중요하게 생각하는 프론트엔드 개발자

0개의 댓글