๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐ŸŒˆ Chapter 10 : ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ด์šฉํ•ด ๊ด€๋ จ ํŒŒ์ผ์„ ๋ชจ์•„๋ผ.

๐ŸŽฏ ๊ฐ€์ ธ์˜ค๊ธฐ์™€ ๋‚ด๋ณด๋‚ด๊ธฐ๋กœ ๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•˜๋ผ.โ€‹

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์„ ์ด์šฉํ•ด ํ”„๋กœ์ ํŠธ์—์„œ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. (Require.js, CommonJS)
  • ๋ชจ๋“ˆ์€ ๋‹จ์ˆœํšŒ๋˜์—ˆ๊ณ , ์ด์ œ๋Š” ๊ฐ„๋‹จํ•œ import ๋ฌธ๊ณผ export ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ์ด ๋‹จ์ˆœํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•˜๋ฉด ํ”„๋กœ์ ํŠธ ๋‚ด์˜ ํŒŒ์ผ ๊ฐ„์— ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๊ฑฐ์˜ ๋™์ผํ•œ ๋ฌธ๋ฒ•์„ ์ด์šฉํ•ด์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๊ณต๊ฐœ๋œ ์ฝ”๋“œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋‹ค์Œ ์˜ˆ์ œ๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ๋ฌธ๋งฅ ํ˜ผ๋™์„ ํ”ผํ•˜๋ผ์—์„œ ์‚ดํŽด๋ดค๋˜ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๊ฐ„๋‹จํ•œ export ๋ฌธ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.
const validator = {
message: '๋Š” ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.',
setInvalidMessages: field => `${field}${this.message}`,
};

export { validator };
  • ํ•จ์ˆ˜, ๋ณ€์ˆ˜, ํด๋ž˜์Šค๋ฅผ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ชจ๋“  ๊ฒƒ์„ ๋‚ด๋ณด๋‚ผ ํ•„์š”๋Š” ์—†์œผ๋ฉฐ, ์—ฌ๋Ÿฌ ํ•จ์ˆ˜ ์ค‘ ์ผ๋ถ€ ํ•จ์ˆ˜๋งŒ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ์—๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ณต๊ฐœ ํ•จ์ˆ˜์™€ ๋น„๊ณต๊ฐœ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•œ ๊ฒƒ๊ณผ ๊ฐ™๋‹ค.
  • ๋‹ค์Œ ์ฝ”๋“œ๋Š” ํ•จ์ˆ˜ ๋‘ ๊ฐœ๋Š” ๋‚ด๋ณด๋‚ด๊ณ , ํ•จ์ˆ˜ ํ•œ ๊ฐœ๋Š” ์ˆจ๊ธฐ๋Š” ๊ฒฝ์šฐ์ด๋‹ค.
function getPower(decimalPlaces) {
return 10 ** decimalPlaces;
}

function capitalize(word) {
return word[0].toUpperCase() + word.slice(1);
}

function roundToDecimalPlace(number, decimalPlaces = 2) {
const round = getPower(decimalPlaces);
return Math.round(number * round) / round;
}

export { capitalize, roundToDecimalPlace };
  • ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด import ํ‚ค์›Œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ค๋ ค๋Š” ํ•จ์ˆ˜๋ฅผ ์ค‘๊ด„ํ˜ธ ์•ˆ์— ์ž‘์„ฑํ•œ๋‹ค.
  • ๊ทธ ๋’ค์— ๊ฐ€์ ธ์˜ฌ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜๋Š”๋ฐ, ํ˜„์žฌ ํŒŒ์ผ์„ ๊ธฐ์ค€์œผ๋กœ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ์ž‘์„ฑํ•œ๋‹ค.
import { capitalize, roundToDecimalPlace } from './util';

function giveTotal(name, total) {
return `${capitalize(name)}๋‹˜, ํ•ฉ๊ณ„๋Š” ${roundToDecimalPlace(total)} ์ž…๋‹ˆ๋‹ค.`;
}

giveTotal('sara', 10.3333333);
// "Sara๋‹˜, ํ•ฉ๊ณ„๋Š” 10.33์ž…๋‹ˆ๋‹ค."
export { giveTotal };
  • ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” ํ•จ์ˆ˜๋งŒ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๊ณ , ๋ณ€์ˆ˜์™€ ํด๋ž˜์Šค๋„ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
const PI = 3.14;
const E = 2.71828;

export { E, PI };
  • ๋‚ด๋ณด๋‚ด๊ธฐ์™€ ๊ฐ€์ ธ์˜ค๊ธฐ๋Š” ํ•ด์ฒด ํ• ๋‹น๊ณผ ๊ฑฐ์˜ ๋™์ผํ•œ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๊ฐ€์ ธ์˜ค๊ธฐ ํ•ญ๋ชฉ์„ ๋ชจ๋‘ ๊ฐ์ฒด์˜ ์†์„ฑ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ๋ณ€์ˆ˜๋ช…์œผ๋กœ ๋ชจ๋“  ๊ฒƒ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ด์ฒด ํ• ๋‹น์˜ ๋ฌธ๋ฒ•๊ณผ ์กฐ๊ธˆ ๋‹ค๋ฅธ ์ ์€ ๋ณ„ํ‘œ(*)๋ฅผ ์ด์šฉํ•ด์„œ ๋ชจ๋“  ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๋ณ€์ˆ˜๋ช…์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ์ฒด์— ์†ํ•œ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
import * as utils from './util.js';

function greet(name) {
return `Hello, ${utils.capitalize(name)}`;
}

console.log(greet('seungmin'));
// Hello, Seungmin
export { greet };
  • ์•„๋ž˜์™€ ๊ฐ™์ด ์„ ์–ธํ•œ ๊ฐ์ฒด๋ฅผ ํŒŒ์ผ์˜ ๋์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋Š” ๋Œ€์‹ , ๊ฐ๊ฐ์˜ ํ•จ์ˆ˜ ์•ž์— export ํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
  • ํŒŒ์ผ ๋๋ถ€๋ถ„์— ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๋” ์‰ฌ์›Œ์ง„๋‹ค.
function getPower(decimalPlaces) {
return 10 ** decimalPlaces;
}

export function capitalize(word) {
return word[0].toUpperCase() + word.slice(1);
}

export function roundToDecimalPlace(number, decimalPlaces = 2) {
const round = getPower(decimalPlaces);
return Math.round(number * round) / round;
}
  • ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋‹จ์ผ ์ง„์ž…์ ์„ ๊ฐ€์ง„ ํŒŒ์ผ์„ ์ž์ฃผ ๋งŒ๋“ค๊ฒŒ ๋  ๊ฒƒ์ด๊ณ , ๋” ์ค‘์š”ํ•œ ํ•จ์ˆ˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์ด๋•Œ ํ•ด๋‹น ํŒŒ์ผ์˜ ๋‚ด๋ณด๋‚ด๊ธฐ ๊ธฐ๋ณธ๊ฐ’(export default)์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ€์ ธ์˜ค๊ธฐ ๊ณผ์ •์ด ์ข€ ๋” ์งง์•„์ง„๋‹ค.
import { capitalize } from './util.js';

export function parseRegion(address) {
const region = address.state || address.providence || '';
return region.toUpperCase();
}

export function parseStreet({ street }) {
return street.split(' ')
.map(part => capitalize(part))
.join(' ');
}

export default function normalize(address) {
const street = parseStreet(address);
const city = address.city;
const region = parseRegion(address);
return `${street} ${city} ${region}`;
}
  • normalize() ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” ์ค‘๊ด„ํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ค‘๊ด„ํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋‚ด๋ณด๋‚ด๊ธฐ ๊ธฐ๋ณธ๊ฐ’๋งŒ ๊ฐ€์ ธ์˜จ๋‹ค.
  • ํ•จ์ˆ˜ ์ด๋ฆ„์„ ๋˜‘๊ฐ™์ด ํ•  ํ•„์š”๋„ ์—†๋‹ค.
  • ๋‚ด๋ณด๋‚ด๊ธฐ ๊ธฐ๋ณธ๊ฐ’์€ ์›ํ•˜๋Š” ๋ณ€์ˆ˜๋ช…์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ๊ฐ™์€ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
import normalize from './address.js';

function getAddress(user) {
return normalize(user.address);
}

export default getAddress;
  • ๋‚ด๋ณด๋‚ด๊ธฐ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ •ํ•ด์ง„ ํ•จ์ˆ˜์™€ ํ•จ๊ป˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋„ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” import ๋ฌธ์„ ํ˜ผํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.
import normalize, { parseRegion } from './address.js';

function getAddress(user) {
return normalize(user.address);
}

export function getAddressByRegion(users) {
// ... ์ƒ๋žต
}
getAddressByRegion(bars);
// {NY: ["1120 Manhattan Ave Brooklyn NY"]}
  • ๊ฐ€์ ธ์˜ค๊ธฐ ๊ธฐ๋ณธ๊ฐ’์€ ํŠนํžˆ ํด๋ž˜์Šค๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์œ ์šฉํ•˜๋‹ค.
  • ํ•œ ๊ฐœ์˜ ํŒŒ์ผ์—๋Š” ํ•œ ๊ฐœ์˜ ํด๋ž˜์Šค๋งŒ ๋‘๋Š” ๊ฒƒ์ด ์ข‹์œผ๋ฏ€๋กœ ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ๋‚ด๋ณด๋‚ผ ๋งŒํ•œ ์•„์œ ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
import { capitalize } from './util.js';

export default class Address {
constructor(address) {
this.address = address;
}

normalize() {
const street = this.parseStreet(this.address);
const city = this.address.city;
const region = this.parseRegion(this.address);
return `${street} ${city}, ${region}`;
}

parseStreet({ street }) {
return street.split(' ')
.map(part => capitalize(part))
.join(' ');
}

parseRegion(address) {
const region = address.state || address.providence || '';
return region.toUpperCase();
}
}

๐ŸŽฏ npm์œผ๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฝ”๋“œ๋ฅผ ๋Œ์–ด์™€๋ผ.โ€‹

  • npm(node package manager)(Node ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž)์ด๋ผ๋Š” ๋„๊ตฌ๋ฅผ ์ด์šฉํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ๋ฅผ ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ์ง์ ‘ ๋‚ด๋ ค๋ฐ›๊ณ , ๋ฒ„์ „์„ ๊ด€๋ฆฌํ•˜๊ณ , ์ต์ˆ™ํ•œ ๊ทœ์น™์— ๋”ฐ๋ผ ๊ฐœ๋ณ„ ํŒŒ์ผ์—์„œ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฌผ๋ก  npm ๋ง๊ณ  ํŽ˜์ด์Šค๋ถ์—์„œ ๋งŒ๋“ , yarn์„ ์‚ฌ์šฉํ•ด npm์„ ๋Œ€์ฒดํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • npm์€ ๋Œ€๋ถ€๋ถ„ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜์ง€๋งŒ, ๊ทธ ์™ธ์—๋„ ํ”„๋กœ์ ํŠธ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋‚˜ ๊ตฌ์„ฑ ์ •๋ณด๋ฅผ ์„ค์ •ํ•˜๊ณ , ๋ช…๋ ฌ์ค„ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฒŒ์‹œํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • npm์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Node.js๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.(Node.js๋ฅผ ์„ค์น˜ํ•˜๋ฉด npm๋„ ์„ค์น˜๋œ๋‹ค.)
  • ๊ทธ ํ›„ ํ”„๋กœ์ ํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด npm init์„ ์‹คํ–‰ํ•ด ์ค€๋‹ค.
  • npm init ๋ช…๋ น์€ package.json ํŒŒ์ผ์˜ ์ƒ์„ฑ์„ ๋„์™€์ฃผ๋Š” ๊ตฌ์„ฑ ๋„๊ตฌ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • package.json ํŒŒ์ผ์—๋Š” ์ด๋ฆ„, ์„ค๋ช…, ๋ผ์ด์„ ์Šค ๋“ฑ๊ณผ ๊ฐ™์€ ํ”„๋กœ์ ํŠธ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ •๋ณด๋ฟ ์•„๋‹ˆ๋ผ, ๋ชจ๋“  ์™ธ๋ถ€ ์˜์กด์„ฑ ์ฝ”๋“œ๋„ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.
  • npm init์€ ๋‹จ์ง€ package.json ํŒŒ์ผ๋งŒ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค๋ฅธ ์ˆจ๊ธด ํŒŒ์ผ์ด๋‚˜ ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
  • package.json ํŒŒ์ผ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ง„์ž…์ ์œผ๋กœ ์™ธ๋ถ€ ์˜์กด์„ฑ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๊ณณ์ด๊ธฐ๋„ ํ•˜๋‹ค.
  • npm์€ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๊ฐ–์ถœ ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•  ๋•Œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋˜ํ•œ, ๋‹ค์šด๋กœ๋“œ ์ˆ˜, ์ˆ˜์ •๋˜์ง€ ์•Š์€ ๋ฒ„๊ทธ์˜ ์ˆ˜, ๋ฒ„์ „ ๋“ฑ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
  • ์กฐ์‚ฌํ•ด ๋ณด๊ณ , ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์“ธ ๋งŒํผ ๋งŒ์กฑ์Šค๋Ÿฝ๋‹ค๋ฉด npm install --save [์›ํ•˜๋Š” ํŒจํ‚ค์ง€] ๋ช…๋ น์„ ์‹คํ–‰ํ•ด ํ”„๋กœ์ ํŠธ์— ์„ค์น˜ํ•œ๋‹ค.
  • --save ํ”Œ๋ž˜๊ทธ๋Š” ๋ฐ˜๋“œ์‹œ ์จ์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ ์Šต๊ด€์ด ๋˜๋ฉด ์ข‹๋‹ค.
  • npm install์„ ํ•˜๋ฉด ํ”„๋กœ์ ํŠธ์— node_modules ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํŒจํ‚ค์ง€๋ฅผ ๋‚ด๋ ค๋ฐ›๋Š”๋‹ค.
  • ๊ทธ๋‹ค์Œ์—๋Š” ์„ค์น˜ํ•˜๋Š” ํŒจํ‚ค์ง€์˜ ๋ฒ„์ „ ๋ฒˆํ˜ธ๋กœ package.json ํŒŒ์ผ์„ ๊ฐฑ์‹ ํ•œ๋‹ค.
  • ๋์œผ๋กœ, ์„ค์น˜ํ•˜๋Š” ์ฝ”๋“œ์˜ ๋ฒ„์ „์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ๋‹ด์€ package-lock.json ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.
  • ๊ฐฑ์‹ ๋œ package.json ํŒŒ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์œผ๋ฉฐ, dependencies ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.20"
}
}
  • ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์€ import ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฝ๋กœ๋Š” ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
./src/merge.js
import lodash, { fromPairs } from 'lodash';

export function mapToObject(map) {
return fromPairs([...map]);
}

export function objectToMap(object) {
const pairs = lodash.toPairs(object);
return new Map(pairs);
}
  • ํ”„๋กœ์ ํŠธ ์–ด๋Š ๊ณณ์—์„œ๋“  ๋™์ผํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์ฝ”๋“œ๋ฅผ ์ฝ์„ ๋•Œ ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์ฝ”๋“œ๋ฒ ์ด์Šค ์™ธ๋ถ€์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ธ์ง€ ์‰ฝ๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ถˆ๋Ÿฌ์˜จ ์ฝ”๋“œ๋Š” ์™ธ๋ถ€ ์ฝ”๋“œ์ด๋‹ค.
  • ์‹คํ™˜๊ฒฝ์„ ์œ„ํ•œ ๋นŒ๋“œ์—์„œ๋Š” ์ œ์™ธํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” npm์ด ๊ฐœ๋ฐœ ์˜์กด์„ฑ์„ ๋‹ค๋ฃจ๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊น”๋”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • npm install --save-dev [์›ํ•˜๋Š” ํŒจํ‚ค์ง€]๋กœ --save-dev ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • package.json์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ•„๋“œ๊ฐ€ ๋‹ค๋ฅด๋‹ค. (devDependencies)
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.20"
},
"devDependencies": {
"prettier": "^2.1.2"
}
}
  • npm ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ด์šฉํ•ด์„œ node_modules ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์„ค์น˜ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • npm run clean์„ ์‹คํ–‰ํ•˜๋ฉด ํ”„๋กœ์ ํŠธ์— ์„ค์น˜ํ•œ Prettier ํŒจํ‚ค์ง€๋ฅผ npm์ด ์‹คํ–‰ํ•ด ์ค€๋‹ค.
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"clean": "prettier --tab-width=2 --write ./code/*.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.20"
},
"devDependencies": {
"prettier": "^2.1.2"
}
}
  • ์ด์ œ ๊ฐœ๋ณ„ ํ”„๋กœ์ ํŠธ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ์„ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋นŒ๋“œ ๊ณผ์ •, ์˜์กด์„ฑ, ํŒจํ‚ค์ง€ ์ •๋ณด๋ฅผ ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

๐ŸŽฏ ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ด์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ผ.โ€‹

  • **์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜(component architecture)**๋กœ ํฉ์–ด์ ธ ์žˆ๋Š” HTML, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ, CSS๋ฅผ ๋ชจ์œผ๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์ž.
  • ์˜ค๋žซ๋™์•ˆ ๊ฐœ๋ฐœ์ž๋“ค์€ ํŒŒ์ผ ํ˜•์‹์— ๋”ฐ๋ผ ๋ถ„๋ฅ˜ํ•˜๊ณค ํ–ˆ๋‹ค. (์ตœ์ƒ์œ„ ๋””๋ ‰ํ„ฐ๋ฆฌ์— css, js, img ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ถ„๋ฅ˜)
  • ๊ทธ๋ ‡์ง€๋งŒ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๊ฐ€ ๋ฐœ์ „ํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ํŒจํ„ด์ด ๋‚˜์™”๋Š”๋ฐ ๋ฐ”๋กœ ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜๋ผ๋Š” ํŒจํ„ด์ด๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ๋Š” ๊ด€๋ จ ์žˆ๋Š” ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์กฐํ•ฉํ•ด ํ•˜๋‚˜์˜ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋‹ด์€ ๊ฒƒ์ด๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์กฐ๊ฐ์„ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์›น ํŽ˜์ด์ง€๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ์˜ ๋ฌธ์ œ๋„ ์žˆ๋Š”๋ฐ ๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” ๋นŒ๋“œ ๋„๊ตฌ์— ์˜์กดํ•œ๋‹ค๋Š” ์ ์ด๋ฉฐ, ๊ทธ๋ณด๋‹ค ๋œํ•˜๊ธฐ๋Š” ํ•˜์ง€๋งŒ ํ”„๋ ˆ์ž„์›Œํฌ์— ์˜์กดํ•œ๋‹ค๋Š” ๋ฌธ์ œ๋„ ์žˆ๋‹ค.
  • ๋‹ค์Œ ์˜ˆ์ œ๋Š” ๋ฆฌ์•กํŠธ๋ฅผ ์‚ฌ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜์ด๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•œ ํŒจํ‚ค์ง€ ํ•˜๋‚˜์— ๋ชจ๋“  ๊ฒƒ์„ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.
import React from 'react';
import './Copyright.css';

export default function CopyrightStatement() {
const year = new Date().getFullYear();
return (
<div className="copyright">
Copyright {year}
</div>
);
}
  • return ๋ฌธ์— ๋งˆํฌ์—…์ด ์žˆ๊ณ , CSS ํด๋ž˜์Šค๋Š” className์— ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค.
  • ์œ„ ์ฝ”๋“œ๋Š” ๋ฆฌ์•กํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์ผ๋ถ€์ธ JSX๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ํŠน๋ณ„ํ•œ ๋งˆํฌ์—…์ด๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ๋กœ๋Š” src/components ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋‹ด๊ฒจ์žˆ๋‹ค.
  • ๋˜ํ•œ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ปดํŒŒ์ผ๋œ ์ฝ”๋“œ๊ฐ€ ๋‹ด๊ธฐ๋Š” public ๋””๋ ‰ํ„ฐ๋ฆฌ๋„ ์žˆ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ๊ฒƒ์€ ๊ฒฐ๊ตญ ๋” ๊ฐ„๋‹จํ•œ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฒฐํ•ฉ๋œ๋‹ค.
  • components ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ๋‹ค๋ค„์•ผ ํ•  ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ด๊ณ  ์žˆ๋‹ค.
  • ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฐœ๋ณ„ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋‚˜๋‰˜์–ด ์žˆ๋‹ค.
  • ๋””๋ ‰ํ„ฐ๋ฆฌ ์ด๋ฆ„์ด ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์€ ๋ฆฌ์•กํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ปจ๋ฒค์…˜์ด๋‹ค.
  • ์ด๋ ‡๊ฒŒ ๋ถˆํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์˜ฎ๊ธฐ๊ณ  ์‹ถ์„ ๋•Œ, ๋””๋ ‰ํ„ฐ๋ฆฌ ์ „์ฒด๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๊ณ , ๋ถˆํ•„์š”ํ•œ CSS๊ฐ€ ์–ด๋”˜๊ฐ€ ๋‚จ์•„์žˆ๋Š” ๊ฒƒ์„ ๊ฑฑ์ •ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ์ตœ๋Œ€ํ•œ ํ•˜๋“œ ์ฝ”๋”ฉํ•˜๋Š” ์„ค์ •์ด ์—†์–ด์•ผ ํ•œ๋‹ค.
  • ํด๋ฆญํ•  ๋•Œ ์–ด๋–ค ๋™์ž‘์ด ํ•„์š”ํ•œ์ง€๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋Œ€์‹  ํด๋ฆญํ•  ๋•Œ ๋™์ž‘์„ ์ปดํฌ๋„ŒํŠธ์— ์ฃผ์ž…ํ•œ๋‹ค.
  • ๋™์ž‘์ด๋‚˜ ์ž์›์„ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ์˜์กด์„ฑ ์ฃผ์ž…์ด๋‹ค. (์˜์กด์„ฑ ์ฃผ์ž…์€ TIP 32 ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์šด ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๋ผ ์ฐธ๊ณ )
import React from 'react';
import './IdeaButton.css';
import idea from './idea.svg';

export default function IdeaButton({ handleClick, message }) {
return (
<button className="idea-button" onClick={handleClick}>
<img className="idea-button_icon" src={idea} alt="idea icon" />
{message}
</button>
);
}
  • ๋ฆฌ์•กํŠธ์—์„œ๋Š” ์ฃผ์ž…๋œ ์˜์กด์„ฑ์„ ํ•จ์ˆ˜์˜ ์ธ์ž๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋˜ํ•œ, ํ•ด์ฒด ํ• ๋‹น์„ ์ด์šฉํ•ด์„œ ๊บผ๋‚ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฒ„ํŠผ์˜ ๋ฉ”์‹œ์ง€๋Š” ์ฃผ์ž…๋œ ๊ฐ’์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ํ‘œ์‹œ๋œ๋‹ค.
  • ์ค‘๊ด„ํ˜ธ๋Š” ํ…œํ”Œ๋ฆฟ ๋ฌธ๋ฒ•์ด๊ณ , ๋ณ€์ˆ˜ ์ •๋ณด๋ฅผ ๊ฐ์‹ธ๊ณ  ์žˆ๋‹ค.
  • ์•„๋ž˜ ์˜ˆ์ œ๋Š” ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ๋กœ ์ด ๊ฒฝ์šฐ ํŽ˜์ด์ง€๋„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.
import React from 'react';

import './App.css';
import IdeaButton from './components/IdeaButton/IdeaButton';
import Copyright from './components/Copyright/Copyright';

function logIdea() {
console.log('์•ˆ๋…•ํ•˜์„ธ์š”! ์Šน๋ฏผ์ž…๋‹ˆ๋‹ค!');
}
export default function App() {
return (
<div className="main">
<div className="app">
<IdeaButton
message="๋‚˜ํ•œํ…Œ ์ข‹์€ ์ƒ๊ฐ์ด ์žˆ์–ด!"
handleClick={logIdea}
/>
</div>
<footer>
<Copyright/>
<IdeaButton
message="์ €๋„ ์ข‹์€ ์ƒ๊ฐ์ด ์žˆ์–ด์š”!"
handleClick={logIdea}
>
</footer>
</div>
);
}
  • ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๋‹ค๋ฅธ ๋ชจ๋“  ์กฐ๊ฐ์„ ํฌํ•จํ•˜๋ฉฐ ๋ชจ๋“  ๊ฒƒ์„ ๊ฒฐํ•ฉํ•œ๋‹ค.
  • ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์ ์ธ ์žฅ์†Œ์— ๋ชจ๋“  ๊ฒƒ์ด ๋ชจ์—ฌ ์žˆ์„ ๋•Œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์ •๋ง ๊ฐ„๋‹จํ•˜๋‹ค.
  • ์ด์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜๋Š” ์ง๊ด€์ ์œผ๋กœ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค.
  • ๊ด€๋ จ๋œ ํŒŒ์ผ์„ ํ•œ๊ณณ์— ๋ชจ์œผ๋Š” ๊ฒƒ์ด๋‹ค.
  • ์œ ์ผํ•œ ์–ด๋ ค์›€์€ ๋ชจ๋“  ๊ฒƒ์„ ์—ฐ๊ฒฐํ•˜๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š๋‹ค๋Š” ์ ์ด๋‹ค.

๐ŸŽฏ ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ์ด์šฉํ•ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฒฐํ•ฉํ•˜๋ผ.โ€‹

  • ๊ธฐ๋ณธ์ ์ธ ๋นŒ๋“œ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ์— ์•ž์„œ์„œ ์ด์ „ ํŒ์—์„œ ์‚ดํŽด๋ดค๋˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋” ๋‹จ์ˆœํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฒ„์ „์„ ์ค€๋น„ํ•œ๋‹ค.
  • ๊ธฐ๋ณธ์ ์ธ ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ
import React from 'react';

import Copyright from './components/Copyright/Copyright';

function App() {
return (
<div className="main">
<footer>
<Copyright />
</footer>
</div>
);
}

export default App;
  • Copyright ์ปดํฌ๋„ŒํŠธ
import React from 'react';

function Copyright() {
const year = new Date().getFullYear();

return (
<div className="copyright">
Copyright {year}
</div>
);
}

export default Copyright;
  • ์ด ํŒŒ์ผ๋“ค์ด ๋‹จ์ˆœํ•˜๊ธฐ๋Š” ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ”๋กœ ์‹คํ–‰ํ•  ์ˆ˜๋Š” ์—†๋‹ค.
  • ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ์‹คํ–‰์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
  • import ๋ฌธ, export ๋ฌธ ๋“ฑ์˜ ES6 ๋ฌธ๋ฒ•๊ณผ JSX๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ํ˜ธํ™˜๋˜๋Š” ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
  • ์ด ๋„๊ตฌ๋Š” ๋ฐ”๋ฒจ(Babel)๋กœ ์ตœ์‹  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. (์ฐธ๊ณ )
  • ๋ฐ”๋ฒจ์€ ES6 ์ดํ›„์˜ ๋ฌธ๋ฒ•์œผ๋กœ ์ž‘์„ฑํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์•„์ง ๋…ผ์˜ ๋‹จ๊ณ„์— ์žˆ๋Š” ๋ฌธ๋ฒ•๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ”๋ฒจ์˜ ๋ช…๋ น์ค„ ์ธํ„ฐํŽ˜์ด์Šค์™€ ํ•จ๊ป˜ ES6 ์ดํ›„์˜ ๋ฌธ๋ฒ•์„ ๋ณ€ํ™˜ํ•  ๋•Œ ํ•„์š”ํ•œ @babel/preset-env, ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ @babel/preset-react๋ฅผ ์„ค์น˜ํ•œ๋‹ค.
$ npm install --save-dev @babel/cli @babel/preset-env @babel/preset-react
  • ๋‹ค์Œ์œผ๋กœ ๊ตฌ์„ฑ ์ •๋ณด๋ฅผ ๋‹ด๊ธฐ ์œ„ํ•ด .babelrc ํŒŒ์ผ์„ ์„ค์ •ํ•œ๋‹ค.
  • ์ด ํŒŒ์ผ์€ ๋ฐ”๋ฒจ์ด ๋‹ค๋ฃฐ ์ฝ”๋“œ์˜ ์ข…๋ฅ˜์™€ ๋ณ€ํ™˜ ๋ฐฉ๋ฒ•์„ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • env๋ฅผ ๋ณด๊ณ  ES6 ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๊ณ , react๋ฅผ ๋ณด๊ณ  ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋„ ๋ณ€ํ™˜ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
.babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
  • package.json ํŒŒ์ผ์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ปดํŒŒ์ผ ์ค€๋น„๊ฐ€ ๋๋‚œ๋‹ค.
  • ์ปดํŒŒ์ผํ•œ ๊ฒฐ๊ณผ๋Š” ํ•˜๋‚˜๋กœ ์ถœ๋ ฅ๋˜์–ด build ๋””๋ ‰ํ„ฐ๋ฆฌ์— bundle.js๋กœ ์ €์žฅ๋œ๋‹ค.
  • ์™„์„ฑ๋œ package.json ํŒŒ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
package.json
{
"name": "test",
"version": "1.0.0",
"description": "Chapter 10",
"main": "index.js",
"scripts": {
"build": "babel src/index.js -o build/bundle.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@babel/cli": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"@babel/preset-react": "^7.10.4",
}
}
  • index.html ํŒŒ์ผ ์ˆ˜์ •
index.html
<!DOCTYPE html>
<html lang="en">

<head>
<title>test</title>
</head>

<body>
<div id="root">
</div>
<script src="./build/bundle.js"></script>
</body>

</html>
  • ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ปดํŒŒ์ผ๋œ bundle.js ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ๋ฐ”๋ฒจ์€ ์ฝ”๋“œ๋ฅผ ๋ณ€ํ™˜ํ•˜์ง€๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ์™€ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” **๋ชจ๋“ˆ ๋กœ๋”(module loader)**๋Š” ๋‚ด์žฅ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค.
  • ๋ชจ๋“ˆ ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์›นํŒฉ(webpack)์„ ์‚ฌ์šฉํ•œ๋‹ค.(์ฐธ๊ณ )
  • ์›นํŒฉ์„ ์ด์šฉํ•˜๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ณ‘ํ•ฉ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ CSS์™€ Sass ์ฒ˜๋ฆฌ, ์ด๋ฏธ์ง€ ๋ณ€ํ™˜๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์›นํŒฉ์—์„œ๋Š” ๋กœ๋”(loader)๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด ํŒŒ์ผ ํ™•์žฅ์ž์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ๋™์ž‘์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค. (๋‹ค์–‘ํ•œ ํŒŒ์ผ ํ˜•์‹์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.)
  • ์›นํŒฉ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ์„ค์น˜๋ฅผ ํ•ด์•ผ๋˜๊ณ , ์›นํŒฉ์„ ์œ„ํ•œ ๋ฐ”๋ฒจ ๋กœ๋”๋„ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฐ”๋ฒจ๋กœ ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•˜๋Š” ๊ฒƒ์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์ด๋ฉฐ, ์ด๋ฅผ ์œ„ํ•ด์„œ babel-loader๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
$ npm install --save-dev babel-loader webpack-cli
  • ๊ทธ๋Ÿฐ ๋‹ค์Œ webpack.config.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.
  • ์ด ํŒŒ์ผ์—๋Š” ์›๋ณธ ์ฝ”๋“œ์˜ ์ง„์ž…์ ๊ณผ ์ปดํŒŒ์ผ์ด ์™„๋ฃŒ๋œ ํŒŒ์ผ์ด ์ถœ๋ ฅ๋  ๊ฒฝ๋กœ๋ฅผ ์„ ์–ธํ•œ๋‹ค.
  • ๋‹ค์Œ์œผ๋กœ ์›นํŒฉ์ด ์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€๋„ ์„ค์ •ํ•œ๋‹ค.
  • ์›นํŒฉ์€ ์ •๊ทœ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•ด ๋กœ๋”๋งˆ๋‹ค ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ํŒŒ์ผ์„ ์ •ํ•œ๋‹ค.
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ํ™•์žฅ์ž๊ฐ€ .js์ธ ํŒŒ์ผ๋งŒ ์ฒ˜๋ฆฌํ• ๋ ค๊ณ  ์„ค์ •ํ•ด์ค€๋‹ค.
webpack.config.js
const path = require('path');

module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.js/,
use: 'babel-loader',
},
],
},
output: {
filename: 'build/bundle.js',
path: path.resolve(__dirname),
},
};
  • ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋Š” ์›นํŒฉ์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด package.json ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.
  • ์›นํŒฉ์€ ์„ค์ •ํ•œ webpack.config.js ํŒŒ์ผ์„ ํ™•์ธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ํ”Œ๋ž˜๊ทธ๋‚˜ ์ธ์ˆ˜๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.
  • package.json์—์„œ ์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฐ”๋ฒจ์„ ์‹คํ–‰ํ•˜๋˜ ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•ด ์›นํŒฉ์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด์ค€๋‹ค.
"scripts": {
"build": "webpack"
},
  • ์ˆ˜์ •๋œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.
$ npm run-script build
  • ์›นํŒฉ์œผ๋กœ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, CSS๋ฅผ ์ปดํŒŒ์ผํ•˜๊ณ  ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • Copyright.js์— CSS๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
import React from 'react';
import './Copyright.css';

function Copyright() {
const year = new Date().getFullYear();

return (
<div className="copyright">
Copyright {year}
</div>
);
}

export default Copyright;
  • CSS ํŒŒ์ผ์„ ํ•ด์„ํ•˜๊ธฐ ์œ„ํ•ด css-loader์™€ ์Šคํƒ€์ผ์„ ํŽ˜์ด์ง€์˜ <head> ์š”์†Œ์— ์ฃผ์ž…ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” style-loader์ด๋‹ค.
$ npm install --save-dev css-loader style-loader
  • webpack.config.js์— style-loader๋ฅผ ๋จผ์ € ์ถ”๊ฐ€ํ•˜๊ณ  ๋‚˜์„œ css-loader๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
webpack.config.js
const path = require('path');

module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
],
},
{
test: /\.js?/,
use: 'babel-loader',
},
],
},
output: {
filename: 'build/bundle.js',
path: path.resolve(__dirname),
},
};
  • npm run-script build๋ฅผ ์‹คํ–‰ํ•œ ๋’ค index.html์„ ํ™•์ธํ•œ๋‹ค.
  • ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ด๋ฏธ์ง€๋Š” ์ปดํŒŒ์ผํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋Œ€์‹ ์— ์›นํŒฉ์œผ๋กœ ํŒŒ์ผ์„ ์˜ฎ๊ธฐ๊ณ  ๊ณ ์œ ํ•œ ์ด๋ฆ„์œผ๋กœ ํŒŒ์ผ๋ช…์„ ๋ฐ”๊พผ๋‹ค.
  • ์›นํŒฉ์€ ๋งˆํฌ์—…์— ์žˆ๋Š” ๊ฒฝ๋กœ๋ฅผ ํŒŒ์ผ์ด ์˜ฎ๊ฒจ์ง„ ๊ฒฝ๋กœ๋กœ ์ž๋™์œผ๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.
  • ์ด๋ฏธ์ง€์— ํŠน๋ณ„ํ•œ ์กฐ์ž‘์„ ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ file-loader๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํŒŒ์ผ์„ ์˜ฎ๊ธฐ๊ณ  ๊ฒฝ๋กœ๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค.
  • ์ด๋ฒˆ์—๋Š” ๋กœ๋”๋ฅผ ๋‹จ์ˆœํžˆ ์„ ์–ธํ•˜์ง€ ์•Š๊ณ , ๋กœ๋”์— ์˜ต์…˜์„ ์ „๋‹ฌํ•œ๋‹ค. ์ฆ‰, ๊ฐ์ฒด๋ฅผ ๋‹ด์€ ๋ฐฐ์—ด์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  • ๊ฐ์ฒด์—๋Š” ๋กœ๋”์™€ ์„ค์ • ์˜ต์…˜์ด ํฌํ•จ๋œ๋‹ค.
  • ํ•„์š”ํ•œ ์˜ต์…˜์€ ์ด๋ฏธ์ง€๋ฅผ ์˜ฎ๊ธธ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฟ์ด๋‹ค.
webpack.config.js
const path = require('path');

module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.svg?/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'build/',
},
},
],
},
// ์ƒ๋žต..
],
},
// ์ƒ๋žต..
};

๐ŸŽฏ CSS ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ํ™œ์šฉํ•˜๋ผ.โ€‹

  • ๊ฐ„๋‹จํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋Œ€์‹  CSS๋กœ ๋Œ€์ฒด๋˜๊ณ  ์žˆ๋‹ค.
  • ๊ธฐ๋ณธ ์˜ˆ์ œ ํŒŒ์ผ์€ ./src/css ํด๋” ์ฐธ๊ณ 
  • ๊ฐ€์žฅ ๋จผ์ € ์šฐ์ธก ๋ฉ”๋‰ด๋ฅผ ์ˆจ๊ธด๋‹ค.
  • .menu ํด๋ž˜์Šค์— ์†์„ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
transform: translateX(calc(300px + 4em + 2px));
  • transform: translateX ์†์„ฑ๊ณผ ๊ฐ’์€ ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” div ๋ฐ–์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ์ด๋™์‹œํ‚ค๊ณ  ์•ˆ ๋ณด์ด๊ฒŒ ๋งŒ๋“ ๋‹ค.
  • ๋ฉ”๋‰ด์˜ ๋„ˆ๋น„(width)์— ์•ˆ์ชฝ ์—ฌ๋ฐฑ(padding)๊ณผ ์™ธ๊ณฝ์„ (border)์˜ ํฌ๊ธฐ๋ฅผ ๋”ํ•œ๋‹ค.
  • ๋ฉ”๋‰ด๋ฅผ ์ˆจ๊ธด ๋’ค ๋‹ค์Œ์œผ๋กœ ํŠธ๋žœ์ง€์…˜(transition)(์ „ํ™˜)์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
  • CSS ํŠธ๋žœ์ง€์…˜์€ ์†์„ฑ ๋ณ€ํ™”๋ฅผ ์ด์šฉํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด๋‹ค. ์ฆ‰, ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ๊ฐ™์€ ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋‘ ์†์„ฑ ๊ฐ„์˜ ์‹œ๊ฐ์ ์ธ ์ „ํ™˜์ผ ๋ฟ์ด๋‹ค.
  • ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ฒ„ํŠผ์— ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค€๋‹ค.
const sidebar = document.getElementById('sidebar');

document.getElementById('show')
.addEventListener('click', () => {
sidebar.classList.toggle('display');
});
  • ๋‹ค์Œ์œผ๋กœ ์Šคํƒ€์ผ์‹œํŠธ์— .menu.display ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
.main {
/* ์ƒ๋žต.. */
transform: translateX(calc(300px + 4em + 2px));
}

.menu.display {
transform: translateX(0);
}
  • ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • CSS ํŠธ๋žœ์ง€์…˜์€ ์ตœ์ดˆ์˜ ์†์„ฑ๊ฐ’์—์„œ ๋งˆ์ง€๋ง‰ ์†์„ฑ๊ฐ’์œผ๋กœ ๋ฐ”๋€” ๋•Œ ํŽ˜์ด์ง€๊ฐ€ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๋ช…๋ น์˜ ๋ชจ์Œ์ด๋‹ค. (MDN ์ฐธ๊ณ )
  • ๋จผ์ € transition-property์— ํŠธ๋žœ์ง€์…˜์„ ์ ์šฉํ•ด์•ผ ํ•  ์†์„ฑ์„ ์ž…๋ ฅํ•œ๋‹ค.
  • ์ด ๊ฒฝ์šฐ transform ์†์„ฑ๋งŒ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์†์„ฑ๊ฐ’์œผ๋กœ transfrom์„ ์ž…๋ ฅํ•œ๋‹ค.
  • ๋‹ค์Œ์œผ๋กœ transition-duration ์†์„ฑ์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง€์† ์‹œ๊ฐ„์„ ์ž…๋ ฅํ•œ๋‹ค.
  • ๋์œผ๋กœ, ํŠธ๋žœ์ง€์…˜์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ์ง€๋ฅผ transition-timing-function ์†์„ฑ์— ์„ค์ •ํ•œ๋‹ค. (MDN ์ฐธ๊ณ )
.menu.display {
transform: translateX(0);
transition-property: transform;
transition-duration: 600ms;
transition-timing-function: linear;
}
  • ํ˜„์žฌ ์ƒํƒœ๋Š” ๋ฉ”๋‰ด๊ฐ€ ์‚ฌ๋ผ์งˆ ๋•Œ ๋ฐ”๋กœ ์‚ฌ๋ผ์ง„๋‹ค. ์ด ๊ฒฝ์šฐ๋Š” ํŠธ๋žœ์ง€์…˜์„ .display ํด๋ž˜์Šค๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์„ ์–ธํ•œ ๊ฒƒ์ด ๋ฌธ์ œ์˜ ์›์ธ์ด๋‹ค.
  • ์ด ๊ฒฝ์šฐ๋Š” ๊ธฐ๋ณธ์ด ๋˜๋Š” .menu ํด๋ž˜์Šค์— ํŠธ๋žœ์ง€์…˜์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.
  • ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
.menu {
/* ์ƒ๋žต.. */
transform: translateX(calc(300px + 4em + 2px));
transition: all 600ms linear;
}