[React] 섹션2: 자바스크립트 새로고침

bien·2024년 9월 5일
0

리액트

목록 보기
2/2

1. 모듈 소개

  • 이번 장에서 학습할 것
    • Core Syntax & Rules
      • 핵심 문법 및 규칙
    • Essential, Modern JavaScript Features
      • 필수적이고 현대적인 JavaScript 기능
    • Key JavaScript Features Used In React Apps
      • React 앱에서 사용되는 주요 JavaScript 기능

2. JavaScript 추가 & 리액트 프로젝트의 차이점

다양한 JavaScript 실행 환경

  • 브라우저에서 (예: 웹사이트의 일부)
    • JavaScript 코드는 어떤 웹사이트에서도 포함될 수 있다.
    • 해당 코드는 브라우저 안에서 실행된다. (즉, 웹사이트 방문자의 기기에서)
  • 모든 컴퓨터에서 (예: 서버 측 코드)
    • Node.jsDeno덕분에 JavaScript코드는 브라우저 외부에서도 실행될 수 있다.
    • 해당 코드는 직접 컴퓨터에서 실행된다.
  • 모바일 장치에서 (예: 내장된 웹사이트를 통해)
    • CapacitorReact Native같은 추가 기술을 사용하면 JavaScript를 기반으로 한 모바일 앱을 만들 수 있다.
    • 해당 코드는 모바일 장치에서 실행된다.

현 강의에서는 브라우저에서의 자바스크립트에 집중한다.

JavaScript 코드를 웹사이트에 추가하기

1. Between <script> Tags

<script>
	alert('Hello');
</script>
  • 유지보수가 어렵고 복잡한 HTML 파일로 만들 수 있음
  • 일반적으로 매우 짧은 스크립트에만 사용됨

2. Via <script> Import

<script src="script.js"></script>
  • HTMLJavaScript 코드를 분리함
  • 복잡한 JS 기반 앱의 유지보수가 더 쉬워짐
  • defer 속성
    • <script src="assets/scripts/app.js" defer></script>
    • import한 JavaScript 코드가 실행되기 시작할 때, HTML 요소가 이미 로드되도록 보장할 수 있다.
      • <script> 태그를 <body>영역의 가장 끝에 두어도 같은 효과를 적용할 수 있다.
  • type속성의 moduel 설정
    • <script src="assets/scripts/app.js" type="module"></script>
    • type="module" 속성을 script 태그에 추가하면, 해당 JavaScript 파일을 모듈로 취급한다.
      • 이때, 모듈로 취급된 자바스크립트 파일은 import가 가능해진다.
        • 즉 다른 스크립트에서 export한 코드를 또 다른 스크립트에 import하는 것이 가능해진다.

3. 리액트를 사용하는 경우

  • 리액트 프로젝트를 작업할 때, HTML 파일에 직접 script 태그를 추가할 일이 거의 없다.
    • 리액트 프로젝트는 대부분 빌드 프로세스를 활용하여 HTML 코드에 자동으로 script 태그를 추가하기 때문이다.

3. 리액트 프로젝트 구축 프로세스

리액트; 빌드 프로세스

  • 리액트 프로젝트는 빌드 프로세스를 사용한다.
    • 이는, 작성한 코드가 브라우저에서 그대로 실행되지 않는 것을 의미한다.
    • 그 대신, 브라우저에 전달되기 전에 내부적으로 코드가 수정된다.
  • 백그라운드에 작동하는 툴에 의해 이 프로세스가 진행된다.
    • package.json파일에서 실제 툴을 확인할 수 있다.
      • 이 파일에는 프로젝트에서 사용하는 모든 의존성, 모든 라이브러리의 목록이 들어있다.
      • 리액트 라이브러리를 구성하는 두 패키지, react, react-dom을 확인할 수 있다.
      • react-scripts: 브라우저에 전달되기 전에 뒤에서 코드를 변환하는 다양한 툴을 제공한다.

  • 실행중인 웹사이트를 보면, 백그라운드 빌드 프로세스가 script 요소를 생성하고 자동으로 HTML파일에 추가한 것을 확인할 수 있다.
    • 크롬 개발자도구에서 수많은 <script> 태그를 확인할 수 있다.
  • 로컬 프로젝트에서는 직접 개발 서버를 실행해야 한다.
    • npm run server를 통해
    • 이 개발 서버가 뒤에서 소스코드를 확인하고 변환해 script요소를 추가한 후 변환된 소스코드를 로드한다.

리액트; 빌드 프로세스 사용 이유

왜 이런 빌드 프로세스를 사용해야 할까?

  1. 처리되지 않은 리액트 코드는 브라우저에서 실행될 수 없기 때문이다.
    • 이는, 리액트 코드가 JSX라는 특별한 기능을 이용하기 때문이다.
    • JSX: JavaScript 파일에 작성된 HTML 코드
      • 표준 자바스크립트 기능이 아니므로 정상적으로 작동하지 않는다.
  2. 작성한 코드가 프로덕션을 위해 최적화되지 않았기 때문이다.
    • 예를들어, 빌드되지 않은 코드는 충분히 간소화되지 않은 코드이다.
      • 간소화; 사용자에게 전달될 수 있을만큼 변수 또는 함수 이름이 짧게 변형되는 것을 의미
      • 샌드박스에 import된 script 파일을 확인하면 아래와 같은데
        • 이는 웹사이트 방문자가 다운로드해야 하는 코드의 양을 최대한 줄일 수 있도록 최적화된 코드이다.
    • create-react-app, vite; 자동 리액트 빌드 프로세스를 포함하고 있어 별도 커스텀 설정이 필요없다.
      • 대신, 리액트 프로젝트를 작업할면 Node.js를 설치해야 한다.

4. import & export

  • 리액트 앱에서는, 여러 파일에 코드를 분리해 관리하기 쉽게 만드는 것이 좋은 방법이다.
    • 이를 위해서, importexport 키워드가 필요하다.

💻 export

  • apiKey 변수 사용.
    • let 키워드를 통해 변수를 생성하고, 스트링을 표현하기 위해 ''""를 사용한다.
    • export 키워드를 통해 외부에서도 해당 변수를 사용할 수 있도록 설정해준다.

util.js

export let apiKey = "ndfizlvjehifz1";

💻 import

  • 사용할 파일에서 import 키워드를 통해 사용한다.
    • 중괄호 사이에 export할 대상의 이름을 입력하면 된다.
      • utils.js에서 변수의 이름이 apiKey였으므로, 그 값을 입력해준다.
    • "" 안에 상대주소를 입력해준다.
      • 순수 JavaScript의 코드인 경우 확장자까지 입력해야 한다.
      • 리액트의 경우 빌드 프로세스가 확장자를 추가하므로 확장자를 입력하지 않고 사용할 수 있다.
    • 순수 JavaScript의 경우 import/export키워드를 사용하려면 type="module"속성을 추가해야 한다.
      • 반면 리액트는 빌드 프로세스가 import, export 키워드가 있는 개별 파일을 모두 합쳐 하나의 큰 파일을 만든 다음 처리하므로, 해당 문법을 지원하지 않는 브라우저에서도 코드를 실행할 수 있다.
        • 브라우저가 작은 JavaScript 파일을 여러개 다운로드하는 대신, 몇 개의 큰 파일을 다운로드하면 되므로 더 효율적이다.

app.js

import { apiKey } from "./util.js";

console.log(apiKey);

💻 default

  • default:import/export를 사용하기 위한 또 다른 방법.
    • 변수를 생성하지 않는다.
      • 따라서 이름을 할당하지 않고, 이름없는 값을 export 해준다.
    • 이 파일에서 export하는 기본값이 된다.
    • 파일별로 하나default export만이 존재할 수 있다.
      • 하나 더 추가하려고 하면 오류가 발생한다.
        • cf. 그냥 export의 경우 여러개 설정 가능
  • 하나의 함수 또는 하나의 값만 export할 때 사용하기 좋은 방법
    • 리액트 컴포넌트에서 하나의 파일에 하나의 컴포넌트, 즉 하나의 JavaScript 함수만 존재하는 경우가 많으므로 이 default 문법을 자주 사용하게 된다.

util.js

// export let apiKey = "ndfizlvjehifz1";
export default "afijlzhielfjzl;vx";
  • export하는 default값에 이름이 할당되어 있지 않으므로, import하려는 파일에서 원하는 이름을 사용할 수 있다.
    • 이름을 반드시 할당해야 한다.
    • 그런 다음 import하려는 파일의 경로를 명시한다.

app.js

// import { apiKey } from "./util.js";
import apiKey from "./util.js";

console.log(apiKey);

💻 여러 개 export/import

export 측

  • default export가 하나만 있다면, 다른 이름있는 export들과 함께 사용해도 문제가 없다.
export default "afijlzhielfjzl;vx";
export let apiKey = "zvizl;fjielzh";
export let abc = "abc";

import 측

  • 중괄호 안에 쉼표로 구분해 여러 대상을 import할 수 있다.
    • 쉼표로 구분하는 대신, *를 사용해 자바스크립트 객체로 묶어 한번에 import할 수도 있다.
    • 사용은 as 키워드로 부여한 이름 뒤에 점을 찍어 export된 대상을 지정할 수 있다.
import { apiKey } from "./util.js";

// import apiKey from "./util.js";
// import { apiKey, abc } from "./util.js";
import * as util from "./util.js";

console.log(util.apiKey);

  • 📌 default
    • 파일별로 하나만 가능
    • import할 때에도 형식이 좀 다르다.
    • 리액트에서는 하나의 컴포넌트를 export하는 일이 많아 default 사용이 빈번한 편

5. 변수와 값

  • 데이터: 값
    • String: ''"", ` 로 감싸진텍스트 문자.
    • Number: 양수 혹은 음수 혹은 실수
    • Boolean: True or False. 상태표현에 사용
    • Null & undefined: 값이 없음을 의미
      • undefined: 아직 값이 할당되지 않은 경우 부여되는 기본값
      • null: 개발자에 의해 명시적으로 할당된 초기화 변수
  • 변수 (let): 값을 저장하는 데이터 컨테이너
    • 원하는 이름을 할당할 수 있다.
      • 코드 내에서 이 이름을 식별자로 사용해, 변수에 저장된 값을 사용할 때 이 이름을 사용한다.
    • 장점1: Reusability (재사용성)
      • 값을 한번만 설정하고, 필요한 장수에서 필요한만큼 다시 사용할 수 있다.
      • 이후 값이 변경되는 경우 수정하기 편리하다.
    • 장점2: Readability (코드 가독성 증진)
      • 코드를 한줄이 아니라 여러 줄에 걸쳐 정리할 수 있다.

let; 변수

let userMessage = "Hello World!!!";

console.log(userMessage);
console.log(userMessgae);
  • 상수 (const): 변수와 마찬가지로 데이터 컨테이너.
    • 단, 값을 다시 할당할 수 없다. 읽기전용
      • 새 값을 할당하려고 시도하면, 오류가 발생한다.
    • 다시 할당하지 않아야 할 값에는 const를 부여해두면 좋다.

const; 상수

const userMessage = "Hello World!!!";

userMessage = "New value"; // **Error*; "userMessage" is read-only*

console.log(userMessage);
console.log(userMessgae);

6. 연산자

JavaScript 에서의 +

  • 텍스트 끼리의 결합에 사용가능
conosle.log("hello" + "world"); // helloworld

===

  • 두 피 연산자가 동일한지 확인하는 연산자.
  • 결과값; 부울
console.log(10 === 5);

7. 함수와 매개변수

  • 함수
    • 나중에 함수를 호출했을 때 실행되는 코드를 정의하는 것
    • 같은 함수를 여러번 호출할 수 있다.
// 함수 정의
function greet() {
  	console.log("Hello!");
}

// 함수 실행
greet();
  • 매개변수
    • 소괄호 안에 표기되는 함수 입력값.
    • 함수를 호출할 때, 입력 매개변수의 값을 제공해야 한다.
    • 매개변수를 사용한다는 것은, 다른 입력값을 할당할 수 있는 함수를 재사용하는 것을 의미한다.
    • 함수 내에서만 사용 가능하다.
    • CamelCase 표기법 사용.
    • = 연산자를 통해 기본값을 할당할 수 있다.
function greetUser(userName, message = "Hello!") {
  	console.log(userName);
  	console.log(message);
}

greetUser("Max");
// Max
// Hello!

greetUser("Manuel", "Hello, what's up?");
// Manuel
// Hello, what's up?
  • 반환값
    • 함수가 값을 반환하도록 설정할 수 있다.
    • return 키워드 사용
function greetUser(userName, message = "Hello!") {
  return "Hi, I am " + userName + ", " + message;
}
  • 함수나 변수는 원하는 이름을 자유롭게 작성할 수 있다.
    • 변수와 상수, 함수가 무슨 작업을 하는지 나타내는 이름을 짓는 것이 좋다.

8. 화살표 함수

  • 화살표 함수
    • 익명 함수를 다룰 때 자주 사용하는 문법
      • 익명함수; 이름이 없이 함수 코드만 있는 함수.
        • 아래 코드 예시에서 화살표 문법을 통해, onClick 리스터의 값으로 함수를 정의했다.
    onClick = {() => setActiveContentIndex(0)}
  • function 키워드를 사용한 익명 함수
export default function() {
  	console.log('Hello');
}
  • 화살표 함수를 이용하는 경우, function 키워드 없이 매개변수 목록만 작성한다.
export default () => {
  	console.log('Hello');
}

export default (userName, messgae) => {
 	console.log('Hello');
  return userName + message;
}

화살표 함수의 구문 단축

1. 매개변수 목록 괄호 생략하기

  • 화살표 함수가 정확히 하나의 매개변수만 사용하는 경우 묶는 괄호를 생략할 수 있다.
(userName) => { ... } // O
userName => { ... } // O
  • 함수에 매개변수가 없는 경우, 괄호를 생략할 수 없다.
    • () => {...}라고 작성해야 한다.
  • 함수가 둘 이상의 매개변수를 받는 경우, 괄호를 생략할 수 없다.
userName, userAge => { ... } // X
(userName, userAge) => { ... } // O

2. 함수 본문 중괄호 생략하기

  • 함수에 반환문 외 다른 로직이 없는 경우, return + {} 생략 가능
number => {
  return number * 3;
} // O

number => number * 3; // O
  • 오류 상황
    • number => return number * 3;
      • 이 경우 return 키워드가 생략되어야 하므로 오류가 발생한다.
    • number => if (number === 2) { return 5 } ;
      • 이 경우 if문은 반환될 수 없으므로 오류가 발생한다.

특수경우; 객체만 반환하는 경우

  • 자바스크립트 객체를 반환하려고 하면, 다음과 같이 유효하지 않은 코드가 나올 수 있다.
number => { age: number }; // 객체를 반환하려고 한다.
  • 자바스크립트는 중괄호를 JS 객체를 생성하는 코드가 아닌 함수 본문 래퍼로 취급하기 때문에, 이 코드는 유효하지 않다.
    • 객체를 생성하고 반환해야 한다고 자바스크립트에게 알리려면, 코드를 아래와 같이 수정해야 한다.
    • 객체와 중괄호를 추가 괄호로 감싸면, 자바스크립트는 중괄호가 객체 생성을 위한 것임을 이해할 수 있다.
number => ({ age: number }); // 추가 괄호를 써서 객체를 감싸준다.

9. 객체와 클래스

  • 객체(Object)
    • 자바스크립트는 객체를 사용해 여러 개의 값을 그룹으로 묶을 수 있다.
      • 키-값 쌍으로 값을 저장할 수 있다.
    • 점 표기법을 통해 객체의 개별 필드에 접근할 수 있다.
    • 메소드(Method)
      • 객체에 함수도 저장할 수 있는데, 이를 일반적으로 메소드라고 부른다.
      • 메소드에서 객체 안의 다른 메소드나 필드를 접근하기 위해서는, this라는 키워드를 사용한다.
const user = {
  	name: "Max",
  	age: 34,
  	greet() {
      console.log("Hello!");
      console.log(this.age)
    }
};

console.log(user.name);
user.greet();
  • 클래스(Class)
    • 나중에 실제 객체를 생성할 때 사용할 청사진을 만들 수 있다.
    • constructor 키워드를 통해 생성자 함수를 추가할 수 있다.
class User {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log('Hi!');
  }
}

const user1 = new User("Manuel", 35);
console.log(user1);
user1.greet();


10. 배열 및 배열 메소드 (map과 같은)

  • 배열 (Array)
    • 값을 목록으로 생성하는 것.
      • 다른 배열, 객체, 숫자, 문자열 등 원하는 모든 값을 저장할 수 있다.
    • 값 만을 순서대로 저장하며, 목록 내 위치에 따라 목록값에 접근할 수 있다.
    • []를 통해 생성하고, 접근 시 []에 원하는 인덱스를 입력해 값에 접근할 수 있다.
    • 자바스크립트에서 아주 중요하고 흔히 사용되는 값 타입.
      • 배열에 저장된 값을 변환하거나 읽는 데 사용할 수 있는 다양한 내장 유틸리티 메소드가 존재한다.
      • 배열 이름에 점 표기법을 붙여 메소드를 조회할 수 있다.
const hobbies = ["Sports", "Cooking", "Reading"];
console.log(hobbies[0]); // Sports

11. 디스트럭처링

배열의 분해

  • 인덱스를 통해 배열의 값에 접근해 값을 분해할 수 있다.
const userNameData = ["Max", "Schwarzmuller"];

const firstName = userNameData[0];
const lastName = userNameData[1];
  • 분해 문법을 이용해, 아래와 같이 바로 값을 가져와 저장할 수 있다.
const [firstName, lastName] = ["Max", "Schwarzumuller"];

console.log(firstName);
console.log(lastName);

객체의 분해

const user = {
  	name: "Max",
  	age: 34
};

const name = user.name;
const age = user.age;
  • 좌측에 중괄호를 사용하고, 우측에 객체를 생성한다.
    • 좌측에서 객체가 분해되는데, nameage를 사용한다.
      • 배열과 달리, 객체에서 정의된 이름을 그대로 가져와야 한다.
      • 객체의 경우 프로퍼티 이름을 기준으로 가져온다.
      • :을 통해 별칭을 부여할 수 있다.
const { name: userName, age } = {
  	name: "Max",
  	age: 34
};

함수 매개변수 목록의 분해

  • 함수 매개변수에서도 분해(디스트럭처링)를 사용할 수 있다.

    • 함수가 객체를 포함하는 매개변수를 수령하는 경우, 객체 프로퍼티를 꺼내어 로컬 범위 변수로 사용할 수 있다.
      • 즉, 함수 본문 내에서만 사용할 수 있다.
  • 예시에서는 점 표기법으로 객체의 프로퍼티에 접근하고 있다.

function setOrder(order) {
  	localStorage.setItem('id', order.id);
  	localStroage.setItem('currency', order.currency);
}
  • 들어오는 객체(즉, setOrder에 인수로 전달된 객체)에서 idcurrency꺼내어 사용한다.
    • 단, 예제에서 setOrder는 여전히 하나의 매개변수만 받고 있다.
      • 매개변수가 2개가 아니라, 내부적으로 디스트럭철이 된 객체 하나만 받고 있다.
function setOrder({id, currency}) { // 디스트럭처링
  localStorage.setItem('id', id);
  localStroage.setItem('currency', currency);
}

setOrder({id: 5, currency: 'USD', amount:15.99}); // 1개의 매개변수

12. 스프레드 연산자

배열의 전개 연산자

  • 전개연산자
    • ...를 사용
    • 배열의 모든 원소를 가져온다.
  • const mergedHobbies = [...hobbies, ...newHobbies];
    • 배열의 값을 가져와, 쉼표로 구분된 개별 값을 새 배열에 추가한다.
const hobbies = ["Sports", "Cooking"];
const newHobbies = ["Reading"];

const copyHobbies = [...hobbies]; // 배열 복사
const nestedHobbies = [hobbies, newHobbies]; // 배열 중첩; 단순히 배열 하나에 두개의 배열이 들어감
const mergedHobbies = [...hobbies, ...newHobbies]; // 배열 원소들을 전개해 하나의 배열에 넣어짐.
배열 복사배열 중첩배열 병합

객체의 전개 연산자

  • 특정 객체에 다른 객체의 프로퍼티를 병합하려고 하는 경우, 전개 연산자를 사용할 수 있다.
const user = {
  	name: "Max",
  	age: 34
};

const extendedUser = {
  	isAdmin: true,
  	...user
};

console.log(extendedUser);
  • user가 전개되어 extendedUser에 추가된다.
    • 전개 연산자가 ...뒤의 객체의 모든 키-값 쌍을 가져와 감싸는 객체에 추가한다.

13. 컨트롤 구조 다시 살펴보기

  • if
const password = prompt("Your passward");

if (password === "Hello") {
  	console.log("Hello works");
} else if (password === "hello") {
  	console.log("hello works");
} else {
  	console.log("Access not granted.");
}
  • forof
const hobbies = ["Sports", "Cooking"];

for (const hobby of hobbies) {
  	console.log(hobby);
}

14. 리액트 없이 DOM 조작하기!

  • 이 강좌에서는 DOM을 직접 조작하는 일이 거의 없다.
    • JavaScript의 강점 중 하나는 웹사이트를 런타임에 읽고 조작하는 능력이다.
      • querySelector등을 사용해 요소를 직접 선택하고 조작할 수 있다.
    • 하지만, React는 선언적 방식으로 코드를 작성하므로, 이런 작업이 거의 필요없다.

15. 함수를 값으로 사용하기

  • JavaScript에서는 함수를 다른 함수에 값으로 전달할 수 있다.
    • 함수를 매개변수로 전달할 때는, 소괄호()를 작성하지 않는다.
      • 소괄호를 추가하면 해당 함수의 반환값이 전달되게 된다.
  • setTimeout함수는 두 개의 매개변수를 받는데, 첫 번째 입력값은 함수이다.
function handleTimeout() {
  	console.log("Timed out!");
}

const handleTimeout2 = () => {
  	console.log("Timed out ... again!");
};

setTimeout(handleTimeout, 2000);
setTimeout(handleTimeout2, 3000);
setTimeout(() => {
  	console.log('More timing out...');
}, 4000); // 필요한 함수를 정의했을 뿐, 바로 실행되는게 아니다.
  • 함수를 다른 함수에 값으로 전달할 수 있다는 것을 이해하는 것은 중요하다.
    • 함수를 일반적인 매개변수처럼 받아, 함수 내부에서 실행할 수 있다.
function greeter(greetFn) {
  	greetFn();
}

greeter(() => console.log("Hi"));

16. 함수 내부에서 함수 정의하기

  • JavaScript는 함수 안에 다른 함수를 정의할 수 있다.
    • 바닐라 자바스크립트 코드에서는 적합하지 않을 수 있지만, 리액트 사용 시에는 적합하다.
function init() {
  function greet() {
    console.log('Hi!');
  }
  
  greet();
}

greet(); // 실행할 수 없음.
init(); // 결과; "Hi!"
  • initgreet을 호출해 한 번의 Hi!만 출력된다.


17. 참조형과 기본값 비교

  • 자바스크립트의 기본형 값은 변경할 수 없다.
    • 스트링(String)에도 호출할 수 있는 내장 메서드가 있다.
      • concat메서드를 사용해 다른 스트링을 연결하면, 기존 스트링을 수정하는 대신 새 스트링이 생성된다.
    • 스트링, 숫자형, 부울을 다룰 때에는 항상 새 값을 생성하게 된다.
      • 기존의 값을 수정할 수 없다.
let userMessage = 'Hello!';
userMessage = 'Hello there!'; // 기존에 메모리에 저장된 스트링은 삭제된다.
userMessage = userMessage.concat('!!!');
  • 객체는 참조형 값이므로, 새로운 값이 생성되는 것이 아니라, 기존 값이 변경된다.
  • 예제에서 const로 선언된 배열 hobbies를 수정할 수 있다. (push를 통해 값을 넣었다.)
    • const는 수정할 수 없음을 의미하지만, 엄밀히 말하자면 변수를 덮어쓸 수 없다는 것을 의미한다.
      • 따라서 const를 사용하면 =연산자를 통해 새 값을 할당할 수 없다.
const hobbies = ["Sprots", "Cooking"];
hobbies = []; // ** error
hobbies.push("Working");
console.log(hobbies);

Reference

  • Udemy: React 완벽 가이드 With Redux, Next.js, TypeScript
profile
Good Luck!

0개의 댓글