for문과 forEach 메서드는 모두 반복문을 작성할 때 사용하지만, 작동 과정이 조금 다릅니다. 이 작동과정의 차이에서 발생하는 차이점에 대해 알아보겠습니다.

for문과 forEach메서드의 차이점 비교

 

for문과 forEach메서드의 차이점 비교

 

0. 여는글

1. `forEach`의 작동 과정

2. `for``문의 작동 과정

3. `forEach` 메서드와 `for`문은 언제 사용해야 될까?

4. 정리하기

 

여는글

 데이터를 저장하기 위해 서버로 보내기 전, 알맞은 값을 넣었는지 확인하는 작업이 필요했다. 그래서 forEach 메서드를 통해 값을 확인한 후, 적절하지 않은 경우에는 alert 창을 띄우고 저장 작업을 멈추도록 했다. 하지만 값에 문제가 있더라도 루프가 계속 진행되었고, 함수의 진행도 멈추지 않았다. 결국 forEach에서의 return문이 내가 생각한 방식과 차이가 있음을 알게 되었다.

const test = [
  { name: "김철수", age: 30 },
  { name: "김영희", age: undefined },
];

const handleSave = async (infos) => {
  // 값의 유효성 확인, 값이 없으면 alert 창 띄우기
  infos.forEach((info) => {
    if (!info.age) {
      console.log(`${info.name}의 나이를 입력해주세요!`);
      return;
    }
  });

  console.log("유효성 검사 통과!");
};

handleSave(test);
// 출력: "김영희의 나이를 입력해주세요!"와 "유효성 검사 통과!"가 모두 출력된다.

1. `forEach`의 작동 과정

 상기한 예시처럼 forEach의 콜백 함수에 return문을 넣더라도 루프가 계속 동작한다. 그 이유는 forEach가 각 요소마다 콜백 함수를 호출하는 방식으로 진행되기 때문이다.


const test2 = [2, 3, 4];
test2.forEach((num) => {
  if (num === 3) {
    console.log('3을 찾았다! 이제 루프 그만 두고 싶어!');
    return;
  }
  console.log('못 찾았다!');
});

 만약 이렇게 작성한 forEach 메서드가 있다고 가정해보겠다. 이러한 경우 아래의 코드와 같은 방식으로 실행된다.


callback(2);
callback(3);
callback(4);

 결국 콜백 함수 내부에 return문을 작성하더라도 그건 전체 반복문에 대한 return이 아닌, 콜백 함수 한 개의 return문이기 때문에 3을 찾은 이후에도 4를 대상으로 콜백 함수가 실행된다.

2. `for`문의 작동 과정

 상기한 `forEach` 메서드를 `for`문으로 바꿔보겠다.

const test2 = [2, 3, 4];

for (const num of test2) {
  if (num === 3) {
    console.log('3을 찾았다! 이제 루프 그만 두고 싶어!');
    return;
  }
  console.log('못 찾았다!');
}

 `for`문 안에서 작성된 `return`문은 전체 반복문을 종료하기에, 3을 찾은 이후에는 루프를 종료한다. 따라서 4를 대상으로는 루프를 진행하지 않는다. 결국 `forEach` 메서드와 `for`문의 차이는 콜백 함수 사용 여부에서 발생한다고 볼 수 있다.

3. `forEach` 메서드와 `for`문은 언제 사용해야 할까?

 `forEach` 메서드와 `for`문은 콜백 함수를 사용하느냐에서 차이가 있다. 그래서 장단점을 비교한다면 이 부분을 고려해서 살펴보면 된다.

 

  • `forEach` 메서드의 장점
    • `for`문에 비해 작성이 편리하다.
    • 코드가 간결하고 명확하여 가독성이 높다.
  • `forEach` 메서드의 단점
    • 배열의 요소가 많아질 시, 실행되는 콜백 함수의 갯수도 증가하므로 자원 소모(오버헤드)가 심해질 수 있다.
    • 각 요소마다 실행되는 콜백 함수의 내부 코드가 복잡해질 경우에도 자원 소모(오버헤드)가 심해질 수 있다.
    • `break`, `continue`를 사용할 수 없어 루프를 중간에 종료하거나 건너뛸 수 없다.

  • `for`문의 장점
    • `break`, `continue`를 통해 루프에 대한 제어가 가능하다.
    • 콜백 함수를 사용하지 않으므로, 배열이 길어지거나 루프 내에 실행하는 코드가 복잡하더라도 오버헤드가 심해지지 않는다.
    • 복잡한 반복 논리를 구현할 때 유리하다.
  • `for`문의 단점
    • 함수형 프로그래밍에서 형태가 다소 이질적이다.
    • 코드가 길어지거나 복잡해질 수 있어 가독성이 떨어질 수 있다.

4. 정리하기

forEach 메서드와 for문은 콜백 함수 사용 여부에서 차이가 있다. forEach 메서드는 작성하기는 간편하지만 성능 면에서는 for문에 밀릴 수 있다. 단순 루프일 경우에는 forEach를 사용해도 괜찮지만, 성능이나 반복문의 제어가 필요한 경우에는 for문을 사용하는 것이 좋다.

'javascript > 이론' 카테고리의 다른 글

자바스크립트의 이벤트 루프  (0) 2024.04.24

자바스크립트에서 비동기 작업을 가능하게 하는 이벤트 루프에 대해 알아봅니다.

자바스크립트의 이벤트 루프

 

자바스크립트의 이벤트 루프

 

1. 이벤트 루프는 왜 필요한가?

2. 이벤트 루프는 어떻게 작동할까?

3. 예시 코드로 살펴보기

 

1. 이벤트 루프는 왜 필요한가?

 자바스크립트는 싱글스레드 언어이다. 따라서 한번에 하나의 작업만 수행한다. 하지만 이 때의 문제는 만약 시간이 오래 걸리는 작업이 있을 시, 전체 서비스가 멈출 수 있다는 것이다. 예를 들어 서버에서 데이터를 모두 받아올 때까지 서비스의 작동이 멈춘다면 어떻게 될까?
 이러한 상황을 해결하기 위한 것이 이벤트 루프다.

2. 이벤트 루프는 어떻게 작동할까?

 알아야 할 주요 개념은 콜스택, 태스크 큐 2가지다. 콜 스택은 함수가 실행될 때, 콜스택에 추가된다. 콜스택에 담긴 작업은 순차적으로 실행된다.
 만약 당장 실행을 끝마치지 않아도 되는 작업 (비동기 작업)일 시 백그라운드로 옮겨 작업을 진행한다. 비동기 작업이 끝난 후 발생한 작업 (콜백 함수, 데이터 전처리 작업 등이 해당)은 태스크 큐에 추가된다.
 이후 콜스택이 비었을 때 테스크 큐에 있는 작업을 콜스택으로 옮겨 진행한다.

3. 예시 코드로 살펴보기

function fetchData() {
  console.log("fetchData 함수 시작");

  fetch("https://example.com").then((response) => {
    console.log("받아온 데이터를 json으로 변환");
    return response.json();
  });

  console.log("fetchData 함수 끝");
}
 상단의 코드가 실행되는 과정은 다음과 같다.

 1. fetchData 함수가 콜스택에 추가된다.
 2. 'fetchData 함수 시작' 문자열이 출력된다.
 3. 서버에서 데이터를 불러오는 작업을 백그라운드로 옮겨 진행한다.
 3 - 1. 데이터를 모두 불러왔으면, '받아온 데이터를 json으로 변환' 문자열을 출력하는 작업을 태스크 큐에 추가한다.
 4. 'fetchData 함수 끝' 문자열을 출력하고, 콜스택에 fetchData 함수를 제거한다.
 5. 콜스택에 비었으므로, 태스크 큐에서 담긴 '받아온 데이터를 json으로 변환' 작업을 옮겨와 실행한다.

'javascript > 이론' 카테고리의 다른 글

for문과 forEach의 차이점 비교  (0) 2024.06.16

 npm이 아닌 yarn 쓰는 이유에 대해 알아봅니다.

yarn은 왜 쓰는가?

 

0. 여는글

1. npm과 yarn

2. yarn은 뭐가 더 좋은가

3. 정리

추천글

 

여는글

 처음 배웠던 것이 npm이었기에 프로젝트에 모듈을 설치할 때에 언제나 'npm start'를 입력하곤 했다. 하지만 입사한 회사에서는 yarn을 사용하고 있었기에 자동반사로 뛰쳐나오는 'npm install'을 억누르는 중이다. npm 대신 yarn을 쓰게 되면 뭐가 더 좋은 걸까?

npm과 yarn

    • 공통점
      • 자바스크립트의 패키지 매니저

    • npm
      • nodejs의 기본 패키지 관리자
      • 'npm install' 사용시 package-lock.json 파일로 의존성 관리

  • yarn
    • facebook에서 개발한 javascript 패키지 관리자
    • 'yarn insall' 사용 시 yarn.lock 파일로 의존성 관리

yarn은 뭐가 더 좋은가

  • 빠른 패키지 설치 (병렬 설치)
    • 여러 패키지를 설치할 때에 동시에 진행됨.
    • npm은 패키지 설치를 순차적으로 진행하므로 yarn을 사용한다면 설치 시간을 단축시킬 수 있음.

  • 오프라인 패키지 설치
    • yarn은 패키지를 설치할 때에 해당 패키지를 캐시에 저장(.yarn-cache 폴더)
    • 이후 오프라인일 때에도 해당 캐시를 이용해 설치 가능

정리

  • 시간을 단축할 수 있다!
    • yarn을 사용한다면 패키지를 병렬 설치할 수 있다는 점, 기존 패키지들이 캐시에 저장된다는 점을 통해 설치 속도를 줄일 수 있다.

추천글

canvas를 사용해 방향키로 좌우 이동 조작할 수 있는 캐릭터를 만들어보았습니다. 움직이는 캐릭터의 이전 모습을 삭제하기 위해 배경과 캐릭터의 캔버스를 나누어 진행하였습니다.

canvas 캐릭터 방향키로 좌우 이동하기

 

 목차

 

1. 구현 화면

2. 고려사항

3. 구현

4. 구현된 코드로 보기

 

1. 구현 화면

방향키를 통해 좌우 이동하는 캐릭터

2. 고려사항

  • 하나의 캔버스만 사용할 시 이전 캐릭터의 그림들을 삭제하기 어려움
    • 이전 캐릭터의 그림을 지우기 위해 해당 좌표를 clearRect로 지울 시 배경까지 같이 삭제되어 버림
    • 따라서, 배경, 캐릭터를 레이어로 나누어 구현
    • 배경은 바닥에, 캐릭터는 공중에 뜬 상태로 그려진다고 볼 수 있음

3. 구현

  • 배경 이미지를 넣을 canvas, 캐릭터를 넣을 canvas 2개 생성
  • 배경 canvas와 캐릭터 canvas가 겹쳐져야 하므로 상위 태그에는 relative, canvas에는 absolute 속성 부여

 

<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 배경 이미지를 넣을 canvas, 캐릭터를 넣을 canvas 2개 생성 -->
    <!-- 배경 canvas와 캐릭터 canvas가 겹쳐져야 하므로 상위 태그에는 relative, canvas에는 absolute 속성 부여 -->
    <div style="position: relative;">
      <canvas id="background" style="position: absolute;"></canvas>
      <canvas id="character" style="position: absolute;"></canvas>
    </div>
    <script src="maple.js"></script>
  </body>
</html>

 

  • 배경 canvas 지정
  • 캐릭터 canvas 지정
  • 배경 제작
    • 배경 경로 불러오기
    • 배경 이미지를 불러오면 배경 canvas에 그리기
    • 이미지를 불러오는 과정에서 비율이 깨지지 않게 이미지의 가로, 세로 길이를 그대로 canvas에 지정
  • 캐릭터 제작
    • 캐릭터 경로 불러오기
    • 캐릭터의 x 좌표 지정
    • 캐릭터 불러오면 캐릭터 canvas에 그리기
    • 캐릭터가 이동할 수 있는 반경은 배경 내부 이므로 배경 이미지의 가로, 세로 길이를 그대로 canvas에 지정
  • canvas에서 키입력 이벤트 설정
    • 캐릭터의 좌표가 0이하면 더이상 왼쪽으로 이동 못하게 제한
    • "캐릭터의 x 좌표 값 + 캐릭터의 가로 길이"가 배경의 가로 길이보다 커질 시 더이상 오른쪽으로 이동 못하게 제한

 

// maple.js

// 배경 canvas 지정
const backgroundCanvas = document.getElementById("background");
const backCtx = backgroundCanvas.getContext("2d");

// 캐릭터 canvas 지정
const characterCanvas = document.getElementById("character");
const chaCtx = characterCanvas.getContext("2d");

// 배경 경로 불러오기
const port = new Image();
port.src =
  "https://github.com/ka0824/ka0824.github.io/assets/79782594/e264a9fb-541b-4cd4-845b-947385a19cef";

// 배경 이미지를 불러오면 배경 canvas에 그리기
// 이미지를 불러오는 과정에서 비율이 깨지지 않기 위해 이미지의 가로, 세로 길이를 그대로 canvas에 적용
port.onload = function () {
  backgroundCanvas.width = port.width;
  backgroundCanvas.height = port.height;
  backCtx.drawImage(port, 0, 0);
};

// 캐릭터 경로 불러오기
const mushroom = new Image();
mushroom.src =
  "https://github.com/ka0824/ka0824.github.io/assets/79782594/1cac660c-db2c-46d0-af9a-ecfebe31b62d";

// 캐릭터의 x 좌표 지정
let mushroomX = 0;

// 캐릭터 불러오면 캐릭터 canvas에 그리기
// 캐릭터가 이동할 수 있는 반경은 배경 내부 이므로 배경 이미지의 가로, 세로 길이를 그대로 적용
mushroom.onload = function () {
  characterCanvas.width = port.width;
  characterCanvas.height = port.height;
  chaCtx.drawImage(mushroom, mushroomX, 100, 40, 50);
};

// canvas에서 키입력 이벤트 설정
document.addEventListener("keydown", function (event) {
  switch (event.key) {
    case "ArrowLeft":
      // 캐릭터의 좌표가 0 이하면 더이상 왼쪽으로 이동 못하게 제한
      if (mushroomX <= 0) {
        return;
      }

      // 이동 속도는 -10, 이동할 때 마다 이전 이미지 지워주기
      mushroomX -= 10;
      chaCtx.clearRect(0, 0, characterCanvas.width, characterCanvas.height);
      chaCtx.drawImage(mushroom, mushroomX, 100, 40, 50);
      return;
    case "ArrowRight":
      // 캐릭터의 가로축이 40이므로, '현재 캐릭터의 x 좌표 값 + 40'이 배경의 가로축보다 커지면 오른쪽으로 이동 못하게 제한
      if (mushroomX + 40 >= characterCanvas.width) {
        return;
      }

      mushroomX += 10;
      chaCtx.clearRect(0, 0, characterCanvas.width, characterCanvas.height);
      chaCtx.drawImage(mushroom, mushroomX, 100, 40, 50);
      return;
    default:
      return;
  }
});

4. 구현된 코드로 보기

'javascript > canvas' 카테고리의 다른 글

canvas로 간단 애니메이션 구현하기  (0) 2023.10.09
캔버스로 기본 도형(2d) 그리기  (0) 2023.10.08

canvas로 애니메이션을 구현해보겠습니다. 좌표를 어떻게 변경해야 할지 고려해 봅시다.

canvas로 간단 애니메이션 구현하기

 

 목차

 

1. 알아두기

2. 직선 이동 애니메이션

3. 원모양 이동 애니메이션

4. 반복되는 애니메이션

5. 왕복 이동 애니메이션

6. 구현 코드로 보기

추천글

 

1. 알아두기

  • x, y 좌표로 이동시키기
    • canvas의 애니메이션은 애니메이션의 x, y좌표를 변경하면서 진행됨
    • 연속되는 그림이 빠르게 지나가면서 애니메이션으로 보여진다라고 생각할 수 있음

 

  • 애니메이션의 속도는 좌표 변경 값의 크기에 따라 정해짐
    • 좌표 변경 값이 클 수록 속도가 빨라지고, 변경 값이 작을 수록 속도가 느려짐

 

  • 이전 화면이 필요 없다면 clearRect 메서드를 이용해 canvas를 지우기
    • clearRect를 사용하지 않으면 이전 화면도 그대로 남아있음. 상황에 따라 사용 여부 결정할 것.

 

  • requestAnimationFrame 함수를 이용하자
    • 작성한 애니메이션 함수를 requestAnimationFrame을 이용해 애니메이션으로 구현할 수 있음

2. 직선 이동 애니메이션

  • 애니메이션 시작 좌표 정하기
  • 좌표 증가 값 정하기 (애니메이션의 속도)
  • 애니메이션 함수 작성
    • 좌표 증가 시키기
    • 달라진 좌표에 맞춰 그림 그리기
    • 애니메이션 맞춤 설정
// lineAnimation.js

const canvas1 = document.getElementById("my-canvas1");
const ctx1 = canvas1.getContext("2d");

// 애니메이션 시작 좌표 정하기
let startX1 = 10;
let startY1 = 10;

const dx1 = 10;

function lineAnimation() {
  // 캔버스 지우기
  // 캔버스의 width, height 값 입력
  ctx1.clearRect(0, 0, canvas1.width, canvas1.height);

  ctx1.fillStyle = "black";

  // 좌표 증가 시키기
  startX1 += dx1;

  // 달라진 좌표에 맞춰서 그림 그리기
  ctx1.fillRect(startX1, startY1, 100, 100);

  // 애니메이션 맞춤 설정
  requestAnimationFrame(lineAnimation);
}

lineAnimation();

3. 원모양 이동 애니메이션

 

  • 시작 각도 지정
  • 각도 늘어나는 값 지정 (애니메이션 속도)
  • 애니메이션 함수 작성
    • 원의 중심 좌표 업데이트
    • 코사인(cos), 사인(sin)을 이용해  x 좌표, y 좌표를 반지름에 맞춰 비율 조정
      • cos(각도) = b / c
      • sin(각도) = a / c
      • 현재 각도에서 생성된 b와 c의 비율 값을 통해 원 외곽의 x 좌표 구하기
      • 현재 각도에서 생성된 a와 c의 비율 값을 통해 원 외곽의 y 좌표 구하기
      • 생성된 x, y 좌표에 경로의 둘레를 얼마나 크게 할 것 인지 값 설정
원 외곽의 x, y 좌표를 구하는 과정
cos(각도) = b / c, 가로 b를 사용하므로 x좌표에 사용

 

sin(각도) = a / c, 세로 a를 사용하므로 y 좌표에 사용

 

// circleAnimation.js

const canvas2 = document.getElementById("my-canvas2");
const ctx2 = canvas2.getContext("2d");

let startX2 = 50;
let startY2 = 50;

// 각도 지정
let angle = 0;

// 애니메이션 속도, 각도가 늘어나는 값
const speed = 0.1;

function circleAnimation() {
  ctx2.clearRect(0, 0, canvas2.width, canvas2.height);

  ctx2.beginPath();

  ctx2.arc(startX2, startY2, 10, 0, 2 * Math.PI);

  ctx2.fillStyle = "white";
  ctx2.fill();
  ctx2.strokeStyle = "black";
  ctx2.lineWidth = 2;
  ctx2.stroke();

  angle += speed;

  // 원의 중심 좌표 업데이트
  // 코사인, 사인을 이용해 x 좌표, y 좌표를 반지름에 맞춰 비율 조정
  // 뒤에 곱하는 값이 커질수록 경로의 둘레가 커짐
  startX2 = 50 + Math.cos(angle) * 40;
  startY2 = 50 + Math.sin(angle) * 40;

  // 다음 프레임 요청
  requestAnimationFrame(circleAnimation);
}

circleAnimation();

 

4. 반복되는 애니메이션

 

  • 직선 이동 애니메이션 재활용
  • 애니메이션 그림이 화면을 벗어나게 되면 좌표 초기화
// infiniteAnimation.js

const canvas3 = document.getElementById("my-canvas3");
const ctx3 = canvas3.getContext("2d");

let startX3 = 10;
let startY3 = 10;
let dx3 = 10;

function infiniteAnimation() {
  ctx3.clearRect(0, 0, canvas3.width, canvas3.height);

  ctx3.fillStyle = "black";

  // 화면을 벗어나게 되면 좌표 초기화
  if (startX3 === canvas3.width) {
    startX3 = 10;
  } else {
    startX3 += dx3;
  }

  ctx3.fillRect(startX3, startY3, 100, 100);

  requestAnimationFrame(infiniteAnimation);
}

infiniteAnimation();

5. 왕복 이동 애니메이션

  • 직선 이동 애니메이션 재활용
  • 이동 방향 결정하는 변수 추가
  • 특정 지점에 도달할 때마다 이동 방향 변경
  • 이동 방향에 따라 좌표 증감
// leftRightAnimation.js

const canvas4 = document.getElementById("my-canvas4");
const ctx4 = canvas4.getContext("2d");

let startX4 = 10;
let startY4 = 10;

let dx4 = 10;

// 이동 방향 결정하는 변수 추가
let isMoveRight = true;

function leftRightAnimation() {
  ctx4.clearRect(0, 0, canvas4.width, canvas4.height);

  ctx4.fillStyle = "black";

  // 특정 지점에 도달할 때마다 이동 방향 변경
  if (startX4 + 100 === canvas4.width) {
    isMoveRight = false;
  } else if (startX4 === 10) {
    isMoveRight = true;
  }

  // 이동 방향에 따라 좌표 증감
  if (isMoveRight) {
    startX4 += dx4;
  } else {
    startX4 -= dx4;
  }

  ctx4.fillRect(startX4, startY4, 100, 100);

  requestAnimationFrame(leftRightAnimation);
}

leftRightAnimation();

6. 구현 코드로 보기

추천글

2023.10.08 - [javascript/canvas] - 캔버스로 기본 도형(2d) 그리기

 

캔버스로 기본 도형(2d) 그리기

캔버스는 웹 페이지에 그래픽을 그리는 데에 사용되며, 이미지, 그래프, 애니메이션 등 시각적 요소를 생성하고 조작할 수 있습니다. 이 포스팅에서는 캔버스를 이용해 2d 기본 도형을 그리는 방

ddmoonddmoon.tistory.com

 

 

'javascript > canvas' 카테고리의 다른 글

canvas 캐릭터 방향키로 좌우 이동하기  (1) 2023.10.12
캔버스로 기본 도형(2d) 그리기  (0) 2023.10.08

 캔버스는 웹 페이지에 그래픽을 그리는 데에 사용되며, 이미지, 그래프, 애니메이션 등 시각적 요소를 생성하고 조작할 수 있습니다. 이 포스팅에서는 캔버스를 이용해 2d 기본 도형을 그리는 방법을 알아보겠습니다.

캔버스로 기본 도형(2d) 그리기

 

 목차

 

1. 캔버스란?

2. 직선 그리기

3. 사각형 그리기

4. 삼각형 그리기

5. 곡선 그리기

6. 원 그리기

7. 구현된 코드로 보기

 

1. 캔버스란?

  • 웹 페이지에 그래픽을 그리는 데 사용
  • 이미지, 그래프, 애니메이션, 게임에 활용 가능
  • 빠른 그래픽 렌더링으로 고성능
  • 복잡한 시각적 효과, 애니메이션에 적용 가능

 

  • 캔버스 생성하기
    • canvas 생성 후, id를 이용해 canvas 변수에 할당하기
    • canvas에서 ctx 가져오기
   <canvas id="my-canvas1" width="400" height="400"></canvas>
   
   <script>
   	
    // canvas 변수에 할당하기
 	const canvas1 = document.getElementById("my-canvas1");
    
    // "context"의 약자로, 캔버스의 요소를 조작할 때 사용
	const ctx1 = canvas1.getContext("2d");
   </script>

2. 직선 그리기

  • 시작점, 끝점 좌표 정하기
  • 선 설정하기
  • 좌표 이으면서 선 그리기
const canvas1 = document.getElementById("my-canvas1");
const ctx1 = canvas1.getContext("2d");

// 시작점, 끝점 좌표 정하기
const startX = 50;
const startY = 50;
const endX = 100;
const endY = 100;

// 선 설정 정하기
ctx1.strokeStyle = "black";
ctx1.lineWidth = 2;

// 선 그리기 시작
ctx1.beginPath();

// 시작점 설정
ctx1.moveTo(startX, startY);

// 끝점 설정
ctx1.lineTo(endX, endY);

// 선 그리기
ctx1.stroke();

3. 사각형 그리기

  • 사각형 스타일 설정하기
  • 사각형 위치, 크기 설정하기
    • ctx.fillRect(x 좌표,  y 좌표, 가로 길이, 세로 길이)
    • fillRect는 색이 채워진 사각형, strokeRect는 테두리만 그려진 사각형이 그려짐
const canvas2 = document.getElementById("my-canvas2");
const ctx2 = canvas2.getContext("2d");

// 사각형 스타일 설정

// 채우기 색상
ctx2.fillStyle = "black"; 

// 테두리 색상
ctx2.strokeStyle = "black"; 

// 테두리 두께
ctx2.lineWidth = 2; 

// 사각형 그리기
// x 좌표, y 좌표, 가로 크기, 세로 크기
// 색깔 채워진 사각형
ctx2.fillRect(50, 50, 100, 100); 

// 테두리만 있는 사각형
ctx2.strokeRect(50, 150, 100, 100);

4. 삼각형 그리기

  • 삼각형의 세 꼭지점 좌표 정한 후 이어주기
const canvas3 = document.getElementById("my-canvas3");
const ctx3 = canvas3.getContext("2d");

ctx3.beginPath();

// 시작점
ctx3.moveTo(100, 50);

// 두번째 점
ctx3.lineTo(150, 150);

// 세번째 점
ctx3.lineTo(50, 150); 

// 경로 닫기
ctx3.closePath(); 

// 테두리 색상
ctx3.strokeStyle = "black"; 

// 테두리 두께
ctx3.lineWidth = 2; 

// 삼각형 채울 색상
ctx3.fillStyle = "blue"; 

// 삼각형 내부 색상 채우기
ctx3.fill(); 

// 삼각형 테두리 그리기
ctx3.stroke();

5. 곡선 그리기

  • quadraticCurveTo() 혹은 bezierCurveTo() 함수 사용
    • quadraticCurveTo(곡선 제어점 x 좌표, 곡선 제어점 y 좌표, 최종 x 좌표, 최종 y좌표)
    • bezierCurveTo(1차 곡선 제어점 x 좌표, 1차 곡선 제어점 y 좌표, 2차 제어점 x, 2차 제어점 y, 최종  x, 최종 y)
const canvas4 = document.getElementById("my-canvas4");
const ctx4 = canvas4.getContext("2d");

ctx4.beginPath();

// 시작점 정하기
ctx4.moveTo(50, 50);

// 두 개의 제어점을 사용한 곡선 그리기
// 첫번째로 꺾을 지점 x 좌표, y좌표, 2번째로 꺾을 지점 x 좌표, y 좌표, 최종 x 좌표, y 좌표
ctx4.bezierCurveTo(50, 100, 150, 20, 250, 50);

ctx4.strokeStyle = "black";
ctx4.lineWidth = 2;

// 곡선을 테두리로 그리기
ctx4.stroke();

6. 원 그리기

  • arc 메서드 사용
    • arc(중심 x 좌표, 중심 y좌표, 반지름, 원의 시작 각도, 원의 끝 각도)
    • 시작 각도, 끝 각도를 통해 원의 일부만 그릴 수도 있음
const canvas5 = document.getElementById("my-canvas5");
const ctx5 = canvas5.getContext("2d");

ctx5.beginPath();

// 중심 x 좌표,  중심 y, 좌표, 반지름, 원의 시작 각도, 원의 끝 각도
// 시작각도, 끝각도를 통해 원의 일부만 그릴 수도 있음
ctx5.arc(100, 100, 50, 0, 2 * Math.PI);

ctx5.fillStyle = "white";
ctx5.fill();
ctx5.strokeStyle = "black";
ctx5.lineWidth = 2;
ctx5.stroke();

7. 구현된 코드로 보기

'Set'은 중복된 값을 허용하지 않는 자료 구조 입니다. Set 자료구조를 사용하면 배열 내에서 중복된 값을 빠르게 제거할 수 있습니다.

Set 이용하여 배열에서 중복되는 값 제외하기

 

 목차

 

1. Set이란?

2. Set 사용법

3. Set으로 배열의 중복된 값 제거하기

 

1. Set이란?

  • Javascript에서 제공하는 내장 객체로, 중복된 값을 허용하지 않는 값들의 집합
  • 중복된 값을 허용하지 않음
  • 순서를 보장하지 않음(인덱스 없음)
  • 고유 메서드 존재 (add, delete, has, clear 등의 메서드 사용)

2. Set 사용법

  • add : 값을 추가
    - 이미 해당 값이 있다면 무시
const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(2); // 이미 중복된 값이 있으므로 무시됨
console.log(mySet); // Set { 1, 2 }
  • delete: 값을 삭제
    - 해당 요소가 없다면 무시
const mySet = new Set([1, 2, 3]);
mySet.delete(2); // 2를 제거
mySet.delete(4); // Set에 없으므로 아무런 작업도 하지 않음
console.log(mySet); // Set { 1, 3 }
  • has: 해당 값이 있는지 확인
    - 있으면 true, 없으면 false 반환
const mySet = new Set([1, 2, 3]);
console.log(mySet.has(2)); // true
console.log(mySet.has(4)); // false
  • clear: 모든 요소 제거
const mySet = new Set([1, 2, 3]);
mySet.clear(); // Set을 비움
console.log(mySet); // Set {}

3. Set으로 배열의 중복된 값 제거하기

  • 해당 배열 Set으로 변환
  • 생성한 Set 다시 배열로 변경
const arr = [1, 2, 1, 4, 2];
const mySet = new Set(arr);
const result = [...mySet];
console.log(result); // [1, 2, 4]

debounce와 throttle은 연속된 사용자의 입력을 처리하는 기술 입니다. debounce는 특정 기간 내의 입력 중 가장 마지막 입력을 처리하고, throttle은 특정 기간 내에 입력이 한번 되면, 이후 받는 다른 입력들은 모두 무시합니다. 이를 통해 성능 최적화 혹은 연속 입력으로 인한 에러를 방지할 수 있습니다.

debounce, throttle로 연속 입력 관리하기 (with react)

 

목차

 

1. debounce란?

2. throttle이란?

3. debounce, throttle 구현하기

4. 구현된 코드로 보기

 

1. debounce란?

  • 연속 이벤트 발생시, 일정한 시간 동안 이벤트가 연속으로 발생 할 시, 마지막 이벤트 발생에만 핸들러를 호출
  • 주로 텍스트 입력, 스크롤 이벤트(새로운 데이터 호출)에 적용
  • 불필요한 처리 혹은 네트워크 요청 방지
  • ex) 사용자가 검색어를 입력할 때 검색 결과 업데이트

2. throttle이란?

  • 일정한 주기로 이벤트 핸들러를 호출
  • 일정 시간 동안 이벤트가 연속 발생하더라도, 맨 처음 한번만 핸들러를 호출하고, 다음 주기에 다시 실행 가능해짐
  • 스크롤 위치에 따라 이벤트를 발생할 경우 사용

3. debounce, throttle 구현하기

  • debounce 함수 작성
    - setTimeout 조작을 위한 변수생성
    - delay 시간 안에 새로운 입력이 들어오면 기존에 있던 setTimeout 실행 취소
    - 새로운 setTimeout  등록

 

/utils/debounce.js

const debounce = (func, delay) => {
  // setTimeout 조작을 위한 변수 생성
  let timerId;

  return (...args) => {
    // delay 시간 안에 새로운 입력이 들어오면 기존에 있던 setTimeout 실행 취소
    clearTimeout(timerId);

    // 새로운 setTimeout 등록하기
    timerId = setTimeout(() => {
      func(...args);
    }, delay);
  };
};

export default debounce;

 

  • throttle 함수 작성
    - setTimeout 조작을 위한 변수 추가
    - delay 시간 안에 새로운 입력이 들어오지 않았을 경우에만 실행, delay 기간 내일 경우 입력 무시
    - delay 시간 이후에 setTimeout 변수 초기화 시켜서 재실행 가능하게끔 처리

 

// utils/throttle.js

const throttle = (func, delay) => {
  // setTimeout 조작을 위한 변수 추가
  let timerId;

  return (...args) => {
    // delay 시간 안에 새로운 입력이 들어오지 않았을 경우에만 실행, delay 기간 내일 경우 입력 무시
    if (!timerId) {
      timerId = setTimeout(() => {
        func(...args);

        // delay 시간 이후에 setTimeout 변수 초기화 시켜서 재실행 가능하게끔 처리
        timerId = undefined;
      }, delay);
    }
  };
};

export default throttle;

 

  • debounce, throttle 적용하기
    - 이벤트 핸들러 내부에서 debounce, throttle 사용 함수 정의하지 않도록 주의
    -  핸들러 내부에서 해당 함수를 정의할 경우, setTimeout 자체가 여러개 생겨 동작 오류가 남

 

import { useState } from "react";
import "./App.css";
import debounce from "./assets/utils/debounce";
import throttle from "./assets/utils/throttle";

function App() {
  const [search1, setSearch1] = useState("");
  const [search2, setSearch2] = useState("");

  // debounce 적용
  const debounceSearch = debounce((value) => setSearch1(value), 1000);

  // throttle 적용
  const throttleSearch = throttle((value) => setSearch2(value), 1000);

  // parameter 넣어주기
  const handleDebounce = (e) => {
    debounceSearch(e.target.value);
  };

  // parameter 넣어주기
  const handleThrottle = (e) => {
    throttleSearch(e.target.value);
  };

  return (
    <div className="App">
      <div className="title">{`< debounce 적용시켰을 때 (1초) >`}</div>

      {/* input에 이벤트 적용 */}
      <input onChange={handleDebounce}></input>
      <div>{`현재 입력된 값은 ${search1} 입니다.`}</div>
      <hr />
      <div className="title">{`< throttle 적용시켰을 때 (1초) >`}</div>

      {/* input에 이벤트 적용 */}
      <input onChange={handleThrottle}></input>
      <div>{`현재 입력된 값은 ${search2} 입니다.`}</div>
      <div className="throttle"></div>
    </div>
  );
}

export default App;

4. 구현된 코드로 보기

+ Recent posts