Potato

리액트에선 컴포넌트 선언 방식은 크게 두 가지로 나뉜다.

첫 번째는 클래스형 컴포넌트

두 번째는 함수형 컴포넌트이다.

현재는 대부분 함수형 컴포넌트로 개발을 진행하지만 과거 프로젝트를 마이그레이션 하거나 유지보수를 하기위해 클래스형 컴포넌트에 대한 개념을 알고 있으면 도움이 된다.

 

특징

클래스형 컴포넌트

1. class 키워드로 시작

2. Component로 상속 받음

3. render() 함수를 사용해서 JSX를 반환

4. props를 조회할 때 this 키워드 사용

 

함수형 컴포넌트

1. JSX를 return문을 사용해서 반환

2. state를 사용할 수 없다

3. 생명주기 함수를 작성할 수 없다.

 

 

마지막으로 두가지의 컴포넌트 선언 방식은 생명주기를 다룰때도 차이가 나는데

클래스형 컴포넌트는 this를 통해 생명주기를 다루며

함수형 컴포넌트는 useState와 useEffect를 이용하여 생명주기를 다루게된다

'WEB > React' 카테고리의 다른 글

[React] state는 왜 setState를 이용하여 변경해야될까  (1) 2023.10.16
[React] Hook이란?  (0) 2023.10.14
[React] Redux 설치 방법 및 세팅  (0) 2023.01.09
[React] Axios 설치 방법 및 사용법  (0) 2023.01.08
[React] useEffect  (0) 2023.01.08

HTML이란? 

HTML 은 Hyper Text Markup Language 약어로 HyperText(웹 페이지에서 다른 페이지로 이동할 수 있도록 하는 것) 기능을 가진 문서를 만드는 언어

 

시멘틱 태그란?

시맨틱(semantic)이라는 '의미의', '의미론적인'라는 뜻을 가진 형용사입니다. 즉 시맨틱 태그는 의미를 부여한 태그

 

시멘틱 태그 종류

<header> 사이트의 머리부분에 사용
<main> 메인 콘텐츠를 나타내는데 사용
<section> 제목별로 나눌 수 있는 문서의 콘텐츠 영역을 구성하는 요소
<article> 개별 콘텐츠를 나타내는 요소
<aside> 좌우측의 사이드 영역
<footer> 사이트의 바닥부분, 주로 연락처나 제작자 정보등을 기술하는 부분
<hgroup> 제목과 부제목을 묶어서 나타내는 요소
<nav> 웹 페이지 메뉴를 만들 때 사용

 

참고링크

https://webclub.tistory.com/608

 

#1 HTML 이란 무엇인가?

HTML 이란 무엇인가? HTML 은 Hyper Text Markup Language 약어로 HyperText(웹 페이지에서 다른 페이지로 이동할 수 있도록 하는 것) 기능을 가진 문서를 만드는 언어입니다. 다시 말해, 구조를 설계할 때 사

webclub.tistory.com

https://coding-factory.tistory.com/883

state란 무엇인가?

간단하게 말해서 state는 변수이다.

state는 일반변수와 다르게 값이 변하면 렌더링이 일어나는 객체임

 

setState를 사용하는 이유

state는 immutble(불변성)을 유지해야 하기 때문이다.
state는 컴포넌트 렌더링에 영향을 주는 데이터를 갖고 있는 객체인데, 이것을 업데이트 하기 위해서는 setState,useState가 필요하다. 직접 state를 수정하면 리액트는 render 함수를 호출하지 않아서 렌더링이 일어나지 않고 setState를 호출하여 state를 변경하면 리액트 엔진이 render 함수를 이용해서 렌더링을 실행하기 때문

 

 

참고링크

https://velog.io/@codns1223/React-state-변경시-setState로-변경해야-하는-이유

'WEB > React' 카테고리의 다른 글

[React] 클래스형 컴포넌트 vs 함수형 컴포넌트  (0) 2024.01.03
[React] Hook이란?  (0) 2023.10.14
[React] Redux 설치 방법 및 세팅  (0) 2023.01.09
[React] Axios 설치 방법 및 사용법  (0) 2023.01.08
[React] useEffect  (0) 2023.01.08

Hook이란

Hook은 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주는 기능

 

Hook이 필요한 이유

클래스형 컴포넌트에서만 가능하던 상태관리를 더 손쉽게 할 수 있기 때문

 

주의사항

 

Hook은 브라우저의 메모리 자원을 사용하기에 함부로 남발하면 오히려 성능저하를 일으킴

 

주요 Hooks

  1. useState (동적 상태 관리)
  2. useEffect (side effect 수행 -mount/unmount/update)
  3. useContext (컴포넌트를 중첩하지 않고도 전역 값 쉽게 관리)

그외 Hooks

  1. useReducer (복잡한 컴포넌트들의 state를 관리 -분리)
  2. useCallback (특정 함수 재사용)
  3. useMemo (연산한 값 재사용)
  4. useRef (DOM선택, 컴포넌트 안에서 조회/수정할 수 있는 변수 관리)
  5. useImperativeHandle
  6. useLayoutEffect
  7. useDebugValue

 

참고링크

https://velog.io/@goyou123/React-Hooks-총정리

호이스팅이란 ?

 

변수나 함수 선언문이 해당 스코프의 맨 위로 옮겨지는것을 뜻한다.

 

호이스팅의 문제점

 

1. 호이스팅이 발생하게되면 개발자가 코드를 작성한 순서와 다르게 동작할 수 있으므로 주의가 필요

2. 변수 선언은 호이스팅되지만, 할당된 값은 그 자리에 남아있지 않고 undefined로 초기화되기 때문에 변수를 사용하기 전에 해당 값을 할당하지 않으면 예상치 못한 결과가 발생할 수 있음

 

호이스팅 방지

 

1. 변수나 함수를 사용하기전 반드시 초기화하는것을 권장

2. var, let대신 const 키워드를 사용하여 블록 스코프 지키기

정규표현식의 코드 구성은 두개의 / 사이에 패턴을 넣는방식이다.

 

정규표현식에서 사용하는 패턴은 다음과 같다.

패턴 의미
a-z 소문자 알파벳이 포함되어있는지
A-Z 대문자 알파벳이 포함되어있는지
ㄱ-ㅎ 자음만 있는 한글이 포함되어있는지
가-힣 한글이 포함되어있는지
0-9 0-9까지의 숫자가 포함되어있는지
\d 숫자가 포함되어있는지
\D 숫자가 포함되어있지 않은지
\w 언더바( _ )와 숫자, 대문자 알파벳, 소문자 알파벳이 포함되어 있는지
\W 언더바( _ )와 숫자, 대문자 알파벳, 소문자 알파벳이 포함되어 있지 않은지
\s 공백(space)이 포함되어 있는지
\S 공백(space)이 포함되어 있지 않은지
\특수문자 해당하는 특수문자가 포함되어있는지

 

 

다음은 데이터 값이 영어 소문자가 포함되어있는지를 확인하는 간단한 예제이다.

const regex = /[a-z]/

console.log(regex.test('abc')); # true
console.log(regex.test('aBc')); # true
console.log(regex.test('ABC')); # false
console.log(regex.test('123')); # false

 

예제에서 알 수 있다시피 조건이 맞다면 true가 반환이 된다.

 

만약 조건을 여러개를 두고 조건에 부합하지않을 때, 무언가를 출력하고싶다면 다음과 같이 코드를 작성하면 된다.

 

const regex = /[\s|ㄱ-ㅎ|가-힣|a-z|A-Z|0-9]/

if (regex.test(data) == false) {
  실행할 코드 ~
}

 

모달외부를 클릭했을 때 모달이 꺼지는 이벤트를 구현하고 싶었다

 

 

<template>
<div ref="List">
	...
</div>
</template>

우선 모달의 범위를 ref를 통해 속성명을 할당시켰다.

 

 

mounted() {
    if (this.open_list_modal) {
      document.addEventListener("click", this.ListoutClick);
    } else {
      document.removeEventListener("click", this.ListoutClick);
    }
  },

그리고 모달창이 열렸을 때, document에 클릭 이벤트를 걸어주었고,

모달창이 닫혔을때 클릭이벤트가 없어지게 하였다.

 

methods: {
    ListoutClick(e) {
      if (this.$refs.List == null) {
        return;
      } else if (!this.$refs.List.contains(e.target)) {
        this.close_list_modal();
      }
    },
}

 

리스트 모달이 켜져 있을 동안 클릭을 하면 그곳이 모달 범위 안인지, 밖인지 확인을 하고

만약 모달범위밖을 클릭했다면 모달창을 끄는 함수를 실행시키도록 하였다.

 

프로젝트 진행 중 방대한 양의 데이터를 한페이지에 표시하기 위해 페이지네이션과 무한스크롤중 하나를 선택했어야 했는데, 해당 프로젝트에서는 모바일 친화적인 UI이기 때문에 페이지네이션보단 무한스크롤을 이용하는게 더 좋다고 판단하여 무한스크롤을 통해 개발을 진행하였다.

 

              <div
                @scroll="InfiniteScroll"
              ></div>

 

 

무한스크롤을 적용하고싶은 div에 @scroll을 통해 해당 div가 스크롤 될 때 마다 이벤트가 실행되게 한다

 

 

InfiniteScroll(e) {
  const { scrollHeight, scrollTop, clientHeight } = e.target;
  if (scrollTop + clientHeight === scrollHeight) {
    this.RequestAPI(this.data);
  }
}

해당 div의 scrollHeight ( 전체 div의 높이 ), scrollTop ( 현재 스크롤의 위치 ), clientHeight ( 현재 보이는 화면의 높이) 를 정의한다.

 

이벤트를 실행할 때 마다 ( 현재 스크롤의 위치 + 현재 보이는 화면의 높이 )가 ( 전체 div의 높이 )와 같은지 확인 한 후 만약같다면 추가적인 api요청을 통해 화면에 뿌려주게 했다.

 

 

 

 

하지만, 개발자환경에서는 정상적으로 동작하였으나 정작 모바일로 보았을 때 추가 api요청이 요청되지 않았다.

처음엔 모바일환경에서는 @scroll이 실행되지 않는 줄 알았으나 안드로이드 adb를 통해 모바일로 개발자환경을 확인한 결과 모바일에서는 스크롤을 전부 내려도 scrollHeight의 높이가 scrollTop + clientHeight의 차이가 대략 0.9정도의 차이가 나는걸 확인했고, 조건을 약간 수정하여서 정상적인 동작이 가능하게 하였다.

 

 

 

개발 중에 검색버튼이 없는 인풋을 구현했는데 인풋값이 바뀔 때 마다 api요청을 보내면 리소스 낭비가 심하기 때문에 마지막 인풋 후 일정시간 이후에 실행되는 디바운스를 구현하였다.

 

<template>      
      <input
        type="text"
        placeholder="Search..."
        class="navbar-input"
        @input="input_search_data = $event.target.value"
      />
</template>

 

<script>
import debounce from "lodash.debounce";

export default {
	...
  data() {
    return {
      input_search_data: "",
    };
  },
  
  watch: {
    input_search_data: debounce(function(e) {
      if (e == '') { 
        return //인풋값을 받았다가 1초안에 다시 지우는 경우 (인풋이 취소된 경우)
      } else {
        this.input_search_data = "" // 인풋값을 초기화하도록 구현하였음
        this.search_data(e) // api요청
      }
    }, 1000),
  },
}

</<script>

 

 

 

loadsh에서 debounce를 임포트 시킨 후

watch를 통해 input_search_data를 봐주면서 마지막 변경 후 1초가 지나면 인풋값으로 api요청을 보내면된다.

 

 

디바운스와 비슷한 용어로 쓰로틀링이 있는데, 이건 다음에 알아보도록 하겠다.

롱 클릭 했을 시 오른쪽과 같은 화면이 된다

프로젝트를 구현하던 중 롱클릭시 해당 이미지가 선택된걸 구현한 뒤, 별을 누르면 첫 번째 이벤트가 발생하고 그 외의 부분을 누르면 두 번째 이벤트가 발생하게 구현하려고 했는데 이벤트 버블링 때문에 별을 누르면 첫 번째 이벤트와 두 번째 이벤트가 동시에 발생했다.

 

검색을 하며 알아보니, Vue에선 이벤트 버블링을 막아주는 기능이 있었는데 바로 click.stop="''"이다.

 

하위에 있는 태그에 click.stop 이벤트를 걸어주면 그 아래에있는 다른 이벤트들이 동작하지 않게 된다.

 

<div>
  <font-awesome-icon
    class="scrap-icon"
    icon="fa-regular fa-star"
    @click="open_list_modal"
    @click.stop="''"
  />
</div>

 

프로젝트를 진행하던 중, 이미지 파일에 롱클릭을 구현했는데, 브라우저에서 지원하는 롱클릭에 의해 정상적인 동작이 힘들어 해당 이벤트를 막는 방법을 찾아 보았다.

 

<script> 안에 다음과 같은 코드를 입력하면 된다.

 

<script>

window.oncontextmenu = function(event) {
  event.preventDefault();
  event.stopPropagation();
  return false;
};

</script>

프로젝트 진행 중, 모달이 생성될 때, 뒤에 있는 상위 컴포넌트가 스크롤되는 부분이 굉장히 어색하고 사용자 입장에서 불편할 것이라고 생각하고 스크롤 제어하는 법을 알아 보았다.

 

처음에 시도 한 것은 css를 통해 모달이 열리는 순간, 상위 컴포넌트의 width와 height를 100%, overflow값을 hidden으로 주는 방법을 시도 해보았는데, 어느정도 휠을 내린 후 모달을 열면 컴포넌트의 크기가 100%로 재조정 되어서 모달을 열기 전화면과 모달을 열고 난 후 보이는 상태가 달라지고 또한 모달이 닫히면 다시 처음위치로 돌아왔기 때문에

다른 방법으로 DOM을 제어하는 방법을 선택했다.

 

 

<script>
import { useStore } from "vuex";
import { computed } from "@vue/runtime-core";

export default {
  name: "SearchView",
  setup() {
    const store = useStore();

    const open_search_modal = computed(
      () => store.state.searchModalStore.open_search_modal
    );

    return {
      open_search_modal,
    };
  },

 

computed를 통해 처음에 store에 있는 search_modal의 값이 true인지 false인지 확인해주고

 

  watch: {
    open_search_modal: function (value) {
      value
        ? (document.body.style.overflow = "hidden")
        : document.body.style.removeProperty("overflow");
    },
  },

 

watch를 통해 해당 값이 true라면 DOM에 overflow hideen을 걸어주고

그게 아니라면 overflow를 주는 방식으로 해결했다.

axios를 통한 비동기 작업을 알아보려고 한다

 

 

1. api 모듈화를 위해 src 하위에 api폴더를 생성 후 index.js와 팀원들이 각자 사용할 API 파일을 생성

 

 

2. index.js 에서 axios 임포트, baseURL과 headers 설정

 

import axios from 'axios';

function apiInstance() {
  const instance = axios.create({
    baseURL: process.env.VUE_APP_API_BASE_URL,
    headers: {
      'Content-Type': 'application/json;charset=utf-8',
    },
  });
  return instance;
}

export { apiInstance };

 

 

3. baseURL 노출을 피하기 위해 env.local 파일 생성

 

 

4. env.local에 baseURL 설정 (배포 전 로컬 사용중)

VUE_APP_API_BASE_URL=http://localhost:8080/

 

 

5. 본인이 사용할 파일에서 다음과 같은 코드 작성

// src/api/Data1.js

import { apiInstance } from './index.js';
const api = apiInstance();

 

 

이제 설정은 끝!

직접 사용해보자.

 

6. 본인의 파일로 dispatch를 통해 actions 호출

 

<template>

</template>

<script>
import { useStore } from "vuex";
import { onBeforeMount } from "@vue/runtime-core";

export default {
  name: "TestView",
  setup() {
    const store = useStore();
    
    onBeforeMount(() => {
      store.dispatch("Member1/getData"),
    });

};
</script>

<style>

</style>

 

7. 모듈화된 store 파일에서 호출될 함수 정의

 

// src/store/modules/Member1.js

const Member1 = {
  namespaced: true,
  state: () => ({
    isLogin: false,
  }),
  mutations: {
    log_in(state) {
      state.isLogin = true
    },
  },
  getters: {},
  actions: {
    getData({ commit }) {
    	getItem(
          (data) => {
            commit('get_result', data)
          },
          (err) => {
            console.log(err);
          }
        )
      },
  },
};

export default Member1;

 

8. API 파일작성 및 export

 

// src/api/Data1.js

import { apiInstance } from './index.js';
const api = apiInstance();

function getItem(res, err) {
  api.get(`요청할 URL`)
    .then(res).catch(err)
}

export getItem

 

 

9. 다시 store 파일로 돌아와 API에서 export 해온걸 임포트

 

// src/store/modules/Member1.js

import getItem from "@/api/Data1";

const Member1 = {
  namespaced: true,
  state: () => ({
    isLogin: false,
  }),
  mutations: {
    log_in(state) {
      state.isLogin = true
    },
  },
  getters: {},
  actions: {
    getData({ commit }) {
    	getItem(
          (data) => {
            commit('get_result', data)
          },
          (err) => {
            console.log(err);
          }
        )
      },
  },
};

export default Member1;

 

10. 해당 axios 작업을 통해 받아온 데이터들을 state에 정의

 

// src/store/modules/Member1.js

import getItem from "@/api/Data1";

const Member1 = {
  namespaced: true,
  state: () => ({
    isLogin: false,
    my_data: null
  }),
  mutations: {
    log_in(state) {
      state.isLogin = true
    },
    get_result(state,data) {
      state.my_data = data
    }
  },
  getters: {},
  actions: {
    getData({ commit }) {
    	getItem(
          (data) => {
            commit('get_result', data)
          },
          (err) => {
            console.log(err);
          }
        )
      },
  },
};

export default Member1;

 

이번에는 vuex4 store 모듈화를 통해 데이터를 사용 변경하는법을 알아보려고 한다.

 

1. 데이터 사용하기

 

1-1. 모듈화한 파일에서 state 저장

const Member1 = {
  namespaced: true,
  state: () => ({
    isLogin: false,
  }),
  mutations: {},
  getters: {},
  actions: {},
};

export default Member1;

 

1-2. 사용하고자하는 컴포넌트에서 useStore 와 computed 임포트

 

<template>

</template>

<script>
import { useStore } from "vuex";
import { computed } from "@vue/runtime-core";

export default {
  name: "TestView",
};
</script>

<style>

</style>

 

 

1-3. setup()에서 사용하고자 하는 state의 데이터를 computed

 

<template>

</template>

<script>
import { useStore } from "vuex";
import { computed } from "@vue/runtime-core";

export default {
  name: "TestView",
  setup() {
    const store = useStore();
    
    const isLogin = computed(
      () => store.state.Member1.isLogin
    );
    
    return {
      isLogin
    };
};
</script>

<style>

</style>

 

 

2. 데이터 변경하기

 

2-1. 변경이 필요한 컴포넌트에서 setup을 통해 commit

 

<template>

</template>

<script>
import { useStore } from "vuex";
import { computed } from "@vue/runtime-core";

export default {
  name: "TestView",
  setup() {
    const store = useStore();
    
    const isLogin = computed(
      () => store.state.Member1.isLogin
    );
    
    const Login = () => {
      store.commit("Member1/log_in")
    };
    
    return {
      isLogin
    };
};
</script>

<style>

</style>

 

 

2-2. 본인이 관리하는 store 파일에서 mutation 코드 작성

 

// src/store/modules/Member1

const Member1 = {
  namespaced: true,
  state: () => ({
    isLogin: false,
  }),
  mutations: {
    log_in(state) {
      state.isLogin = true
    },
  },
  getters: {},
  actions: {},
};

export default Member1;

 

 

다음 포스팅에서는 모듈화 상태에서 axios를 통한 비동기 작업을 알아보려고 한다.

Vue3를 통한 프로젝트 중 Store모듈화의 필요성을 느끼고 공부하게 되었다.

 

 

1. Vuex4 설치

npm install --save vuex@4.0.1

 

2. index.js에 다음과 같은 코드 작성

 

// src/store/index.js

const store = new Vuex.Store({
  modules: {
  },
});
export default store;

 

3. modules 라는 폴더에 각자 필요한 데이터들을 저장하고 사용할 파일생성

설명을 위한 예시, 파일이름은 자유

4. 자신이 사용할 파일에서 다음과 같은 스켈레톤 코드 입력

// src/store/modules/Member1.js

const Member1 = {
  namespaced: true,
  state: () => ({
  }),
  mutations: {},
  getters: {},
  actions: {},
};

export default Member1;

 

5. 해당 파일을 index.js에서 임포트

 

// src/store/index.js

import Vuex from 'vuex';

// 작성한 모듈을 가져오기
import Member1 from '@/store/modules/Member1.js';

const store = new Vuex.Store({
  modules: {
    Member1,
  },
});
export default store;

 

 

다음 포스팅에서는 모듈화한 데이터를 컴포넌트에서 사용하는법과 변경하는법을 알아보려고 한다.

+ Recent posts