Ecoer Logo

@caesium133

32

Developer & Designer

steemit.com/@caesium133
VOTING POWER100.00%
DOWNVOTE POWER100.00%
RESOURCE CREDITS100.00%
REPUTATION PROGRESS30.91%
Net Worth
0.621USD
STEEM
0.001STEEM
SBD
1.153SBD
Effective Power
5.001SP
├── Own SP
0.962SP
└── Incoming Deleg
+4.039SP

Detailed Balance

STEEM
balance
0.001STEEM
market_balance
0.000STEEM
savings_balance
0.000STEEM
reward_steem_balance
0.000STEEM
STEEM POWER
Own SP
0.962SP
Delegated Out
0.000SP
Delegation In
4.039SP
Effective Power
5.001SP
Reward SP (pending)
0.000SP
SBD
sbd_balance
1.153SBD
sbd_conversions
0.000SBD
sbd_market_balance
0.000SBD
savings_sbd_balance
0.000SBD
reward_sbd_balance
0.000SBD
{
  "balance": "0.001 STEEM",
  "savings_balance": "0.000 STEEM",
  "reward_steem_balance": "0.000 STEEM",
  "vesting_shares": "1566.187790 VESTS",
  "delegated_vesting_shares": "0.000000 VESTS",
  "received_vesting_shares": "6577.472016 VESTS",
  "sbd_balance": "1.153 SBD",
  "savings_sbd_balance": "0.000 SBD",
  "reward_sbd_balance": "0.000 SBD",
  "conversions": []
}

Account Info

namecaesium133
id751066
rank311,733
reputation6488197104
created2018-02-11T04:38:12
recovery_accountsteem
proxyNone
post_count7
comment_count0
lifetime_vote_count0
witnesses_voted_for0
last_post2018-02-17T15:54:09
last_root_post2018-02-15T07:23:06
last_vote_time2018-03-10T17:49:51
proxied_vsf_votes0, 0, 0, 0
can_vote1
voting_power0
delayed_votes0
balance0.001 STEEM
savings_balance0.000 STEEM
sbd_balance1.153 SBD
savings_sbd_balance0.000 SBD
vesting_shares1566.187790 VESTS
delegated_vesting_shares0.000000 VESTS
received_vesting_shares6577.472016 VESTS
reward_vesting_balance0.000000 VESTS
vesting_balance0.000 STEEM
vesting_withdraw_rate0.000000 VESTS
next_vesting_withdrawal1969-12-31T23:59:59
withdrawn0
to_withdraw0
withdraw_routes0
savings_withdraw_requests0
last_account_recovery1970-01-01T00:00:00
reset_accountnull
last_owner_update1970-01-01T00:00:00
last_account_update2018-02-13T04:01:00
minedNo
sbd_seconds404,146,089
sbd_last_interest_payment2018-02-22T01:04:48
savings_sbd_last_interest_payment2018-02-26T02:39:42
{
  "active": {
    "account_auths": [],
    "key_auths": [
      [
        "STM4tf95EmPxwCny9Mp4qjVmnGLMGKNMmbk1jzgZQd7jH5QFAAZJV",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "balance": "0.001 STEEM",
  "can_vote": true,
  "comment_count": 0,
  "created": "2018-02-11T04:38:12",
  "curation_rewards": 0,
  "delegated_vesting_shares": "0.000000 VESTS",
  "downvote_manabar": {
    "current_mana": 2035914951,
    "last_update_time": 1779056751
  },
  "guest_bloggers": [],
  "id": 751066,
  "json_metadata": "{\"profile\":{\"location\":\"Republic of Korea\",\"website\":\"https://parksb.github.io/\",\"profile_image\":\"https://i.imgur.com/PyMw8Cb.png\",\"name\":\"Cs133\",\"about\":\"Developer & Designer\"}}",
  "last_account_recovery": "1970-01-01T00:00:00",
  "last_account_update": "2018-02-13T04:01:00",
  "last_owner_update": "1970-01-01T00:00:00",
  "last_post": "2018-02-17T15:54:09",
  "last_root_post": "2018-02-15T07:23:06",
  "last_vote_time": "2018-03-10T17:49:51",
  "lifetime_vote_count": 0,
  "market_history": [],
  "memo_key": "STM6vCF37qihYRr2AAjPRvgzctQUz4MfKkWA8s91KWTprv6XCSKVT",
  "mined": false,
  "name": "caesium133",
  "next_vesting_withdrawal": "1969-12-31T23:59:59",
  "other_history": [],
  "owner": {
    "account_auths": [],
    "key_auths": [
      [
        "STM6uqvzdojd3tWQMLxrgkFJqrvBCcNU25b5fLirKTeLZgZjdznAh",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "pending_claimed_accounts": 0,
  "post_bandwidth": 0,
  "post_count": 7,
  "post_history": [],
  "posting": {
    "account_auths": [],
    "key_auths": [
      [
        "STM5VPFtrdSdJTUoEDiMGVqeviTpQVng7VhXGr2fkGy2PJNWdj97K",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "posting_json_metadata": "{\"profile\":{\"location\":\"Republic of Korea\",\"website\":\"https://parksb.github.io/\",\"profile_image\":\"https://i.imgur.com/PyMw8Cb.png\",\"name\":\"Cs133\",\"about\":\"Developer & Designer\"}}",
  "posting_rewards": 532,
  "proxied_vsf_votes": [
    0,
    0,
    0,
    0
  ],
  "proxy": "",
  "received_vesting_shares": "6577.472016 VESTS",
  "recovery_account": "steem",
  "reputation": "6488197104",
  "reset_account": "null",
  "reward_sbd_balance": "0.000 SBD",
  "reward_steem_balance": "0.000 STEEM",
  "reward_vesting_balance": "0.000000 VESTS",
  "reward_vesting_steem": "0.000 STEEM",
  "savings_balance": "0.000 STEEM",
  "savings_sbd_balance": "0.000 SBD",
  "savings_sbd_last_interest_payment": "2018-02-26T02:39:42",
  "savings_sbd_seconds": "0",
  "savings_sbd_seconds_last_update": "2018-02-26T02:39:42",
  "savings_withdraw_requests": 0,
  "sbd_balance": "1.153 SBD",
  "sbd_last_interest_payment": "2018-02-22T01:04:48",
  "sbd_seconds": "404146089",
  "sbd_seconds_last_update": "2018-03-01T02:39:42",
  "tags_usage": [],
  "to_withdraw": 0,
  "transfer_history": [],
  "vesting_balance": "0.000 STEEM",
  "vesting_shares": "1566.187790 VESTS",
  "vesting_withdraw_rate": "0.000000 VESTS",
  "vote_history": [],
  "voting_manabar": {
    "current_mana": "8143659806",
    "last_update_time": 1779056751
  },
  "voting_power": 0,
  "withdraw_routes": 0,
  "withdrawn": 0,
  "witness_votes": [],
  "witnesses_voted_for": 0,
  "rank": 311733
}

Withdraw Routes

IncomingOutgoing
Empty
Empty
{
  "incoming": [],
  "outgoing": []
}
From Date
To Date
steemdelegated 4.039 SP to @caesium133
2026/05/17 22:25:51
delegateecaesium133
delegatorsteem
vesting shares6577.472016 VESTS
Transaction InfoBlock #106141271/Trx f9330b10faa5ff32ba7c9041abcc2783639f4971
View Raw JSON Data
{
  "block": 106141271,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "6577.472016 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-05-17T22:25:51",
  "trx_id": "f9330b10faa5ff32ba7c9041abcc2783639f4971",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 2.374 SP to @caesium133
2026/05/11 20:35:33
delegateecaesium133
delegatorsteem
vesting shares3865.261611 VESTS
Transaction InfoBlock #105967037/Trx e17a5bcbab3f3b0648f207c5c0d63929c17f0677
View Raw JSON Data
{
  "block": 105967037,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "3865.261611 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-05-11T20:35:33",
  "trx_id": "e17a5bcbab3f3b0648f207c5c0d63929c17f0677",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemdelegated 4.047 SP to @caesium133
2026/04/25 21:49:36
delegateecaesium133
delegatorsteem
vesting shares6589.987772 VESTS
Transaction InfoBlock #105508975/Trx df12411770abd1ffac42d1e6d4ef84504734ea80
View Raw JSON Data
{
  "block": 105508975,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "6589.987772 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-04-25T21:49:36",
  "trx_id": "df12411770abd1ffac42d1e6d4ef84504734ea80",
  "trx_in_block": 2,
  "virtual_op": 0
}
steemdelegated 2.399 SP to @caesium133
2026/01/23 03:00:03
delegateecaesium133
delegatorsteem
vesting shares3906.808430 VESTS
Transaction InfoBlock #102846065/Trx 7fc61e59091c4b862f226ba501cdaea3dd02c16d
View Raw JSON Data
{
  "block": 102846065,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "3906.808430 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2026-01-23T03:00:03",
  "trx_id": "7fc61e59091c4b862f226ba501cdaea3dd02c16d",
  "trx_in_block": 6,
  "virtual_op": 0
}
steemdelegated 2.500 SP to @caesium133
2024/12/16 22:19:24
delegateecaesium133
delegatorsteem
vesting shares4071.027627 VESTS
Transaction InfoBlock #91292472/Trx 8aba885766ed10789fffb60f6eb402af58e5edb7
View Raw JSON Data
{
  "block": 91292472,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "4071.027627 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2024-12-16T22:19:24",
  "trx_id": "8aba885766ed10789fffb60f6eb402af58e5edb7",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 2.604 SP to @caesium133
2023/11/13 14:04:24
delegateecaesium133
delegatorsteem
vesting shares4240.161159 VESTS
Transaction InfoBlock #79846733/Trx d01859dc836aeba7c2b98f68c0bb2d9c63acb636
View Raw JSON Data
{
  "block": 79846733,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "4240.161159 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2023-11-13T14:04:24",
  "trx_id": "d01859dc836aeba7c2b98f68c0bb2d9c63acb636",
  "trx_in_block": 3,
  "virtual_op": 0
}
steemdelegated 4.408 SP to @caesium133
2023/09/21 19:42:51
delegateecaesium133
delegatorsteem
vesting shares7177.439945 VESTS
Transaction InfoBlock #78345303/Trx d72d3ba33b668a9b81655dceb51797223a774f33
View Raw JSON Data
{
  "block": 78345303,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "7177.439945 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2023-09-21T19:42:51",
  "trx_id": "d72d3ba33b668a9b81655dceb51797223a774f33",
  "trx_in_block": 5,
  "virtual_op": 0
}
steemdelegated 4.544 SP to @caesium133
2022/11/03 09:44:42
delegateecaesium133
delegatorsteem
vesting shares7399.121383 VESTS
Transaction InfoBlock #69110923/Trx 3aec168b1ab3aa4d28d0780469a5a56a9c22a0c0
View Raw JSON Data
{
  "block": 69110923,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "7399.121383 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2022-11-03T09:44:42",
  "trx_id": "3aec168b1ab3aa4d28d0780469a5a56a9c22a0c0",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemdelegated 4.679 SP to @caesium133
2022/01/17 09:10:09
delegateecaesium133
delegatorsteem
vesting shares7619.654614 VESTS
Transaction InfoBlock #60807283/Trx e56ba817d4c2c43029d37c361d66030fe0519886
View Raw JSON Data
{
  "block": 60807283,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "7619.654614 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2022-01-17T09:10:09",
  "trx_id": "e56ba817d4c2c43029d37c361d66030fe0519886",
  "trx_in_block": 13,
  "virtual_op": 0
}
steemdelegated 4.792 SP to @caesium133
2021/06/13 23:09:48
delegateecaesium133
delegatorsteem
vesting shares7803.423272 VESTS
Transaction InfoBlock #54605761/Trx d72bd42ed0d6bab24a1afd75ea7121db85cf44e4
View Raw JSON Data
{
  "block": 54605761,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "7803.423272 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2021-06-13T23:09:48",
  "trx_id": "d72bd42ed0d6bab24a1afd75ea7121db85cf44e4",
  "trx_in_block": 6,
  "virtual_op": 0
}
steemdelegated 4.907 SP to @caesium133
2020/12/11 09:31:00
delegateecaesium133
delegatorsteem
vesting shares7990.845246 VESTS
Transaction InfoBlock #49353284/Trx fb086030bf86da6a71d5d310f802cb32152c6b9d
View Raw JSON Data
{
  "block": 49353284,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "7990.845246 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-12-11T09:31:00",
  "trx_id": "fb086030bf86da6a71d5d310f802cb32152c6b9d",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemdelegated 1.174 SP to @caesium133
2020/12/06 03:08:30
delegateecaesium133
delegatorsteem
vesting shares1912.543513 VESTS
Transaction InfoBlock #49204854/Trx 055bd8ab19515ba93853f2a31705243ab710ad55
View Raw JSON Data
{
  "block": 49204854,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "1912.543513 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-12-06T03:08:30",
  "trx_id": "055bd8ab19515ba93853f2a31705243ab710ad55",
  "trx_in_block": 0,
  "virtual_op": 0
}
steemdelegated 4.911 SP to @caesium133
2020/12/05 11:05:21
delegateecaesium133
delegatorsteem
vesting shares7997.211885 VESTS
Transaction InfoBlock #49185957/Trx b67934f36917794cf913a73faa9d0c0197ea90bd
View Raw JSON Data
{
  "block": 49185957,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "7997.211885 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-12-05T11:05:21",
  "trx_id": "b67934f36917794cf913a73faa9d0c0197ea90bd",
  "trx_in_block": 9,
  "virtual_op": 0
}
steemdelegated 1.179 SP to @caesium133
2020/11/02 12:06:36
delegateecaesium133
delegatorsteem
vesting shares1920.017158 VESTS
Transaction InfoBlock #48253649/Trx 12255b2f3b1fe0182d2da3592f9a161ee2038c69
View Raw JSON Data
{
  "block": 48253649,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "1920.017158 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-11-02T12:06:36",
  "trx_id": "12255b2f3b1fe0182d2da3592f9a161ee2038c69",
  "trx_in_block": 1,
  "virtual_op": 0
}
steemdelegated 5.035 SP to @caesium133
2020/05/09 04:03:51
delegateecaesium133
delegatorsteem
vesting shares8199.858459 VESTS
Transaction InfoBlock #43215074/Trx 1312da2390e1a2c515d20c6a1d95e16e2b7da07e
View Raw JSON Data
{
  "block": 43215074,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "8199.858459 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-05-09T04:03:51",
  "trx_id": "1312da2390e1a2c515d20c6a1d95e16e2b7da07e",
  "trx_in_block": 11,
  "virtual_op": 0
}
steemdelegated 1.200 SP to @caesium133
2020/05/08 07:25:09
delegateecaesium133
delegatorsteem
vesting shares1953.311140 VESTS
Transaction InfoBlock #43190879/Trx a60372935017b25c36e3858bc0ee6161d18346d6
View Raw JSON Data
{
  "block": 43190879,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "1953.311140 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-05-08T07:25:09",
  "trx_id": "a60372935017b25c36e3858bc0ee6161d18346d6",
  "trx_in_block": 4,
  "virtual_op": 0
}
steemdelegated 5.036 SP to @caesium133
2020/05/07 17:38:00
delegateecaesium133
delegatorsteem
vesting shares8200.663501 VESTS
Transaction InfoBlock #43174723/Trx ad3cdb0e15619166fa04aac69370c0e76295fbfd
View Raw JSON Data
{
  "block": 43174723,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "8200.663501 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-05-07T17:38:00",
  "trx_id": "ad3cdb0e15619166fa04aac69370c0e76295fbfd",
  "trx_in_block": 10,
  "virtual_op": 0
}
2020/02/11 05:53:27
authorsteemitboard
bodyCongratulations @caesium133! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@caesium133/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table> <sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@caesium133) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=caesium133)_</sub> ###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorcaesium133
parent permlink21-3
permlinksteemitboard-notify-caesium133-20200211t055326000z
title
Transaction InfoBlock #40717819/Trx 9e2fcd829c174c467f25b714c47facb5f39f865a
View Raw JSON Data
{
  "block": 40717819,
  "op": [
    "comment",
    {
      "author": "steemitboard",
      "body": "Congratulations @caesium133! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@caesium133/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table>\n\n<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@caesium133) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=caesium133)_</sub>\n\n\n###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "caesium133",
      "parent_permlink": "21-3",
      "permlink": "steemitboard-notify-caesium133-20200211t055326000z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2020-02-11T05:53:27",
  "trx_id": "9e2fcd829c174c467f25b714c47facb5f39f865a",
  "trx_in_block": 4,
  "virtual_op": 0
}
steemdelegated 5.156 SP to @caesium133
2019/06/02 22:45:39
delegateecaesium133
delegatorsteem
vesting shares8396.010096 VESTS
Transaction InfoBlock #33459236/Trx a21188986d78d5c30d556919fedc3b01e07d6967
View Raw JSON Data
{
  "block": 33459236,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "8396.010096 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2019-06-02T22:45:39",
  "trx_id": "a21188986d78d5c30d556919fedc3b01e07d6967",
  "trx_in_block": 8,
  "virtual_op": 0
}
2019/02/11 08:16:42
authorsteemitboard
bodyCongratulations @caesium133! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@caesium133/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table> <sub>_[Click here to view your Board](https://steemitboard.com/@caesium133)_</sub> > Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
json metadata{"image":["https://steemitboard.com/img/notify.png"]}
parent authorcaesium133
parent permlink21-3
permlinksteemitboard-notify-caesium133-20190211t081641000z
title
Transaction InfoBlock #30248777/Trx 104aa06ae8fa510f8cdb85e847de8078c5b6748c
View Raw JSON Data
{
  "block": 30248777,
  "op": [
    "comment",
    {
      "author": "steemitboard",
      "body": "Congratulations @caesium133! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@caesium133/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table>\n\n<sub>_[Click here to view your Board](https://steemitboard.com/@caesium133)_</sub>\n\n\n> Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!",
      "json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
      "parent_author": "caesium133",
      "parent_permlink": "21-3",
      "permlink": "steemitboard-notify-caesium133-20190211t081641000z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2019-02-11T08:16:42",
  "trx_id": "104aa06ae8fa510f8cdb85e847de8078c5b6748c",
  "trx_in_block": 10,
  "virtual_op": 0
}
steemdelegated 5.278 SP to @caesium133
2018/06/09 19:52:36
delegateecaesium133
delegatorsteem
vesting shares8595.286207 VESTS
Transaction InfoBlock #23180014/Trx f409ee5d6476f458b3669e4b7300886638a9aaba
View Raw JSON Data
{
  "block": 23180014,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "8595.286207 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-06-09T19:52:36",
  "trx_id": "f409ee5d6476f458b3669e4b7300886638a9aaba",
  "trx_in_block": 50,
  "virtual_op": 0
}
suhdevelloperupvoted (100.00%) @caesium133 / 21-1
2018/05/24 04:54:00
authorcaesium133
permlink21-1
votersuhdevelloper
weight10000 (100.00%)
Transaction InfoBlock #22701733/Trx b1f825ea0edb25fa554a018705238c2d2639e76c
View Raw JSON Data
{
  "block": 22701733,
  "op": [
    "vote",
    {
      "author": "caesium133",
      "permlink": "21-1",
      "voter": "suhdevelloper",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-05-24T04:54:00",
  "trx_id": "b1f825ea0edb25fa554a018705238c2d2639e76c",
  "trx_in_block": 4,
  "virtual_op": 0
}
talhadogansent 0.001 STEEM to @caesium133- "Hello! ✔ RESTEEM to 7.000+ FOLLOWERS and Your Post Will Be UPVOTED By 45+ ACCOUNTS │ │Send 0.500 SBD or 0.500 STEEM to @talhadogan (URL as Memo) │ │100% Upvote by @talhadogan 💚 7/24 Service Active 💚"
2018/05/03 20:02:09
amount0.001 STEEM
fromtalhadogan
memoHello! ✔ RESTEEM to 7.000+ FOLLOWERS and Your Post Will Be UPVOTED By 45+ ACCOUNTS │ │Send 0.500 SBD or 0.500 STEEM to @talhadogan (URL as Memo) │ │100% Upvote by @talhadogan 💚 7/24 Service Active 💚
tocaesium133
Transaction InfoBlock #22115219/Trx a8ffeebe3f6c39a7ef8f42cd7f591d20b130c2dd
View Raw JSON Data
{
  "block": 22115219,
  "op": [
    "transfer",
    {
      "amount": "0.001 STEEM",
      "from": "talhadogan",
      "memo": "Hello! ✔ RESTEEM to 7.000+ FOLLOWERS and Your Post Will Be UPVOTED By 45+ ACCOUNTS │ │Send 0.500 SBD or 0.500 STEEM to @talhadogan (URL as Memo) │ │100% Upvote by @talhadogan 💚 7/24 Service Active 💚",
      "to": "caesium133"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-05-03T20:02:09",
  "trx_id": "a8ffeebe3f6c39a7ef8f42cd7f591d20b130c2dd",
  "trx_in_block": 58,
  "virtual_op": 0
}
2018/03/10 17:51:57
authorleesunmoo
permlinksteem-sbd-krw
votercaesium133
weight10000 (100.00%)
Transaction InfoBlock #20559371/Trx 20dde7d48b0dcb81479a2081c64f49a43b60c594
View Raw JSON Data
{
  "block": 20559371,
  "op": [
    "vote",
    {
      "author": "leesunmoo",
      "permlink": "steem-sbd-krw",
      "voter": "caesium133",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-03-10T17:51:57",
  "trx_id": "20dde7d48b0dcb81479a2081c64f49a43b60c594",
  "trx_in_block": 53,
  "virtual_op": 0
}
2018/03/10 17:49:51
authorlukebrn
permlinkthe-issue-with-ethereum-and-its-tokens-scaling
votercaesium133
weight10000 (100.00%)
Transaction InfoBlock #20559329/Trx 9a9197c465f61795c3455da73221f6d2c89d3863
View Raw JSON Data
{
  "block": 20559329,
  "op": [
    "vote",
    {
      "author": "lukebrn",
      "permlink": "the-issue-with-ethereum-and-its-tokens-scaling",
      "voter": "caesium133",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-03-10T17:49:51",
  "trx_id": "9a9197c465f61795c3455da73221f6d2c89d3863",
  "trx_in_block": 38,
  "virtual_op": 0
}
caesium133blockchain operation: fill transfer from savings
2018/03/01 02:39:42
amount1.153 SBD
fromcaesium133
memo
request id1519612778
tocaesium133
Transaction InfoBlock #20282221/Virtual Operation #26
View Raw JSON Data
{
  "block": 20282221,
  "op": [
    "fill_transfer_from_savings",
    {
      "amount": "1.153 SBD",
      "from": "caesium133",
      "memo": "",
      "request_id": 1519612778,
      "to": "caesium133"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-03-01T02:39:42",
  "trx_id": "0000000000000000000000000000000000000000",
  "trx_in_block": 4294967295,
  "virtual_op": 26
}
caesium133blockchain operation: transfer from savings
2018/02/26 02:39:42
amount1.153 SBD
fromcaesium133
memo
request id1519612778
tocaesium133
Transaction InfoBlock #20195997/Trx 3e8aec5c2a8afaa10502905b420b4e2c9038a4b5
View Raw JSON Data
{
  "block": 20195997,
  "op": [
    "transfer_from_savings",
    {
      "amount": "1.153 SBD",
      "from": "caesium133",
      "memo": "",
      "request_id": 1519612778,
      "to": "caesium133"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-26T02:39:42",
  "trx_id": "3e8aec5c2a8afaa10502905b420b4e2c9038a4b5",
  "trx_in_block": 9,
  "virtual_op": 0
}
caesium133blockchain operation: transfer to savings
2018/02/26 02:39:21
amount1.153 SBD
fromcaesium133
memo
tocaesium133
Transaction InfoBlock #20195990/Trx 34538a6b6bafdc80299a104a3ef9c16f67fa5340
View Raw JSON Data
{
  "block": 20195990,
  "op": [
    "transfer_to_savings",
    {
      "amount": "1.153 SBD",
      "from": "caesium133",
      "memo": "",
      "to": "caesium133"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-26T02:39:21",
  "trx_id": "34538a6b6bafdc80299a104a3ef9c16f67fa5340",
  "trx_in_block": 32,
  "virtual_op": 0
}
t3ran13sent 0.020 SBD to @caesium133- "Reward authors from The Alternative STEEM TOPs https://steemit.com/top/@t3ran13/the-alternative-steem-tops-13-02-2018-gmt-top-of-the-pop-newbies"
2018/02/22 13:11:12
amount0.020 SBD
fromt3ran13
memoReward authors from The Alternative STEEM TOPs https://steemit.com/top/@t3ran13/the-alternative-steem-tops-13-02-2018-gmt-top-of-the-pop-newbies
tocaesium133
Transaction InfoBlock #20093452/Trx 43e50cd2a6ff7aa99a08b6682c8c1b01621821a2
View Raw JSON Data
{
  "block": 20093452,
  "op": [
    "transfer",
    {
      "amount": "0.020 SBD",
      "from": "t3ran13",
      "memo": "Reward authors from The Alternative STEEM TOPs https://steemit.com/top/@t3ran13/the-alternative-steem-tops-13-02-2018-gmt-top-of-the-pop-newbies",
      "to": "caesium133"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-22T13:11:12",
  "trx_id": "43e50cd2a6ff7aa99a08b6682c8c1b01621821a2",
  "trx_in_block": 55,
  "virtual_op": 0
}
steemdelegated 17.864 SP to @caesium133
2018/02/22 01:21:06
delegateecaesium133
delegatorsteem
vesting shares29090.673493 VESTS
Transaction InfoBlock #20079252/Trx cbeb46f57182b09886d85509700d792a2aa8ffc9
View Raw JSON Data
{
  "block": 20079252,
  "op": [
    "delegate_vesting_shares",
    {
      "delegatee": "caesium133",
      "delegator": "steem",
      "vesting_shares": "29090.673493 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-22T01:21:06",
  "trx_id": "cbeb46f57182b09886d85509700d792a2aa8ffc9",
  "trx_in_block": 18,
  "virtual_op": 0
}
caesium133claimed reward balance: 1.108 SBD, 0.326 SP
2018/02/22 01:04:48
accountcaesium133
reward sbd1.108 SBD
reward steem0.000 STEEM
reward vests531.442566 VESTS
Transaction InfoBlock #20078926/Trx 40941cfd68f27a738836ab0fe33fc421985f4eeb
View Raw JSON Data
{
  "block": 20078926,
  "op": [
    "claim_reward_balance",
    {
      "account": "caesium133",
      "reward_sbd": "1.108 SBD",
      "reward_steem": "0.000 STEEM",
      "reward_vests": "531.442566 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-22T01:04:48",
  "trx_id": "40941cfd68f27a738836ab0fe33fc421985f4eeb",
  "trx_in_block": 3,
  "virtual_op": 0
}
caesium133received 1.108 SBD, 0.326 SP author reward for @caesium133 / 21-1
2018/02/20 02:52:18
authorcaesium133
permlink21-1
sbd payout1.108 SBD
steem payout0.000 STEEM
vesting payout531.442566 VESTS
Transaction InfoBlock #20023484/Virtual Operation #6
View Raw JSON Data
{
  "block": 20023484,
  "op": [
    "author_reward",
    {
      "author": "caesium133",
      "permlink": "21-1",
      "sbd_payout": "1.108 SBD",
      "steem_payout": "0.000 STEEM",
      "vesting_payout": "531.442566 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-20T02:52:18",
  "trx_id": "0000000000000000000000000000000000000000",
  "trx_in_block": 4294967295,
  "virtual_op": 6
}
caesium133claimed reward balance: 0.025 SBD, 0.008 SP
2018/02/18 08:10:12
accountcaesium133
reward sbd0.025 SBD
reward steem0.000 STEEM
reward vests12.265299 VESTS
Transaction InfoBlock #19972269/Trx 98957a0e69213e719cbb45b1b0f151f1ea2f3fbb
View Raw JSON Data
{
  "block": 19972269,
  "op": [
    "claim_reward_balance",
    {
      "account": "caesium133",
      "reward_sbd": "0.025 SBD",
      "reward_steem": "0.000 STEEM",
      "reward_vests": "12.265299 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-18T08:10:12",
  "trx_id": "98957a0e69213e719cbb45b1b0f151f1ea2f3fbb",
  "trx_in_block": 14,
  "virtual_op": 0
}
caesium133received 0.025 SBD, 0.008 SP author reward for @caesium133 / 63ddrw
2018/02/18 04:56:51
authorcaesium133
permlink63ddrw
sbd payout0.025 SBD
steem payout0.000 STEEM
vesting payout12.265299 VESTS
Transaction InfoBlock #19968401/Virtual Operation #7
View Raw JSON Data
{
  "block": 19968401,
  "op": [
    "author_reward",
    {
      "author": "caesium133",
      "permlink": "63ddrw",
      "sbd_payout": "0.025 SBD",
      "steem_payout": "0.000 STEEM",
      "vesting_payout": "12.265299 VESTS"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-18T04:56:51",
  "trx_id": "0000000000000000000000000000000000000000",
  "trx_in_block": 4294967295,
  "virtual_op": 7
}
2018/02/17 15:54:09
authorcaesium133
bodyThank you :)
json metadata{"tags":["font-end"],"app":"steemit/0.1"}
parent authorhappymikes
parent permlinkre-caesium133-21-2-webpack-20180215t213535903z
permlinkre-happymikes-re-caesium133-21-2-webpack-20180217t155407014z
title
Transaction InfoBlock #19952749/Trx b635ea39df6a4167ffed71b6f68c765e74cb4d1e
View Raw JSON Data
{
  "block": 19952749,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "Thank you :)",
      "json_metadata": "{\"tags\":[\"font-end\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "happymikes",
      "parent_permlink": "re-caesium133-21-2-webpack-20180215t213535903z",
      "permlink": "re-happymikes-re-caesium133-21-2-webpack-20180217t155407014z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-17T15:54:09",
  "trx_id": "b635ea39df6a4167ffed71b6f68c765e74cb4d1e",
  "trx_in_block": 0,
  "virtual_op": 0
}
2018/02/15 12:45:51
authorhappymikes
bodyUpvoted ☝ Have a great day!
json metadata
parent authorcaesium133
parent permlink21-2-webpack
permlinkre-caesium133-21-2-webpack-20180215t213535903z
titleCongratulations
Transaction InfoBlock #19891433/Trx b633b7f95fc6c555f6676492283a5c42ee81a9b9
View Raw JSON Data
{
  "block": 19891433,
  "op": [
    "comment",
    {
      "author": "happymikes",
      "body": "Upvoted ☝ Have a great day!",
      "json_metadata": "",
      "parent_author": "caesium133",
      "parent_permlink": "21-2-webpack",
      "permlink": "re-caesium133-21-2-webpack-20180215t213535903z",
      "title": "Congratulations"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T12:45:51",
  "trx_id": "b633b7f95fc6c555f6676492283a5c42ee81a9b9",
  "trx_in_block": 39,
  "virtual_op": 0
}
2018/02/15 12:39:45
authorcaesium133
permlink21-2-webpack
voterhappymikes
weight300 (3.00%)
Transaction InfoBlock #19891311/Trx 738f6fb2bf37cccd66a7235839af1609ce24066f
View Raw JSON Data
{
  "block": 19891311,
  "op": [
    "vote",
    {
      "author": "caesium133",
      "permlink": "21-2-webpack",
      "voter": "happymikes",
      "weight": 300
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T12:39:45",
  "trx_id": "738f6fb2bf37cccd66a7235839af1609ce24066f",
  "trx_in_block": 22,
  "virtual_op": 0
}
caesium133published a new post: 21-1
2018/02/15 07:24:54
authorcaesium133
body![Modern software development](https://i.imgur.com/KzCHMAx.gif) 솔직히 프론트엔드 개발에 대해 이 움짤보다 더 잘 설명할 자신이 없습니다. 당장 [2018년 웹 개발자 로드맵](https://github.com/kamranahmedse/developer-roadmap)만 봐도 배울게 넘칩니다. (그리고 지금 이 시간에도 계속해서 늘어나고 있습니다.) 이제 막 프론트엔드 개발을 시작하려는 분이나 오래전에 웹 개발을 배운 뒤 정보를 업데이트하지 않은 분들은 아득한 기분이 들겁니다. 그래도 막상 시작해보면 생각만큼 어렵지는 않습니다. 모든 것이 서로 얽혀있기 때문에 하나를 배우면 동시에 다른 하나를 같이 배우게 됩니다. 이번 글에서만 npm과 npm scripts, webpack을 다룹니다. 너무 걱정하지 마세요. # 오래된 방법 처음 웹 프로그래밍을 배울 때는 HTML 파일에 외부 CSS나 자바스크립트 코드를 `<link>`태그나 `<script>`태그로 가져오라고 배웁니다. 이렇게요: <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="index.js"></script> </body> </html> 이정도만 해도 웹사이트는 잘 돌아갑니다. 하지만 많은 점이 불편합니다. * 만약 자바스크립트 코드가 방대해져서 파일을 30개로 쪼갠다면 `index.html`에는 30개의 script태그가 필요하게 됩니다. CSS도 마찬가지구요. * 뿐만 아니라 외부 자바스크립트 라이브러리를 가져올 때도 문제가 생깁니다. 만일 jQuery 3.3.1 버전을 다운받아서 `<script src="jquery-3.3.1.min.js"></script>`와 같은 코드를 작성한다면 jQuery 3.4.0 버전이 나왔을 땐 다시 코드를 다운받는 수고를 해야합니다. * 다른 사람과 프로젝트를 공유할 때도 불편합니다. 어떤 파일이 어떤 라이브러리나 프레임워크를 사용하고 있는지 일일히 설명해야하고, 또 그 파일들을 모두 공유해야 하니까요. # npm(node package manager) 사용하기 그래서 npm이라고 하는 '패키지 매니저'를 사용합니다. (원래는 대부분 [Bower](https://bower.io/)를 사용했는데, 이젠 npm이 더 인기있습니다.) npm은 마치 앱스토어에서 어플리케이션을 다운받아 쓰듯이 자바스크립트 패키지를 쉽게 찾고 다운받아서 웹사이트에 적용시킬 수 있도록 만들어줍니다. 이렇게 하면 해당 웹사이트가 어떤 패키지를 사용하고 있는지 한 눈에 볼 수 있고, 패키지의 버전 관리도 훨씬 쉬워집니다. npm은 Node.js를 기반으로 합니다. [Node.js 홈페이지](https://nodejs.org/ko/)에서 안정적인 LTS버전을 다운받으세요. Node.js를 설치하면 npm도 설치됩니다. npm을 사용한다는 것은 CLI 환경에서 작업을 한다는 것을 의미합니다. 많은 분들이 커맨드 라인을 보면 겁을 먹는데, 너무 걱정하지 않으셔도 됩니다. 익숙해지면 오히려 GUI보다 편합니다. 자, 설치가 끝났다면 이제 터미널이나 cmd를 열고 잘 설치되었는지 확인해보세요. Node.js 버전 확인: $ node -v npm 버전 확인: $ npm -v 이제 21세기 프론트엔드 개발 환경 세팅에 첫 삽을 떴습니다. `index.html`이 있는 작업 디렉토리로 이동해서 npm 환경을 초기화해줍니다. $ npm init 이것저것 설정하는 질문이 뜰텐데 그냥 모두 엔터를 눌러주세요. 나중에 수정할 수 있습니다. 설정이 끝났다면 `index.html`이 있는 디렉토리에 `package.json`이라는 새로운 파일이 생깁니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } 이제 npm으로 jQuery를 설치해볼까요? $ npm install jquery 이 명령어를 실행하면 npm의 중앙 저장소에서 jQuery를 다운받아 `node_modules`라는 폴더에 저장합니다. `package.json`을 새로고침하면 내용이 바뀐 것을 볼 수 있습니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "jquery": "^3.3.1" } } 정말 편합니다. 이제 다른 사람과 프로젝트를 함께 할 때 `node_modules` 폴더를 주지 않고 `package.json` 파일을 주면 상대방은 `$ npm install` 명령어를 통해 필요한 자바스크립트 패키지를 한번에 다운받을 수 있게 됩니다. `node_modules/jquery/dist/`폴더를 보면 jQuery 코드들이 모여있는 것을 볼 수 있습니다. <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="node_modules/jquery/dist/jquery.min.js"></script> <script src="index.js"></script> </body> </html> 이렇게 하면 `index.js`에서 jQuery를 사용할 수 있습니다. 하지만 패키지를 설치하고 불러 올때마다 `node_moduels` 폴더를 뒤져봐야 하는 것은 여전히 불편합니다. 하지만 이것도 쉽게 해결할 수 있습니다. // index.js var $ = require('jquery'); console.log($('#title').text()); `jquery`는 전역에서 사용 가능하며, Node.js는 서버사이드 언어이기 때문에 패키지의 경로를 따로 지정해주지 않아도 모두 알아서 패키지를 가져와 줍니다. `index.html`에서 jQuery를 불러오는 `<script>`태그를 지우고 사이트를 새로고침해보세요. 어떤가요? 아쉽지만 브라우저는 `require`를 모르기 때문에 에러가 뜹니다. (브라우저는 파일 시스템에 접근할 권한을 가지고 있지 않습니다.) 그래서 우리는 webpack를 사용합니다. # webpack 사용하기 webpack은 '모듈 번들러'입니다. [Browserify](http://browserify.org/)라는 모듈 번들러도 있는데, webpack은 번들링 뿐만 아니라 부가적인 기능을 많이 제공하고 있어 매우 편리합니다. 번들링이라는 것은 여러 자바스크립트 파일들을 하나로 합친다는 의미입니다. 현재 `index.html`에서는 `jquery.min.js`와 `index.js`를 따로 불러들이고 있는데, webpack을 사용하면 `bundle.js`와 같은 파일 하나만 부를 수 있게 됩니다. 또한 앞서 사용하지 못한 `require`도 사용할 수 있게 됩니다. 그럼 webpack을 설치해봅니다. $ npm install webpack --save-dev `--save-dev`은 개발 의존으로 설치하겠다는 의미입니다. 즉, webpack이 개발용으로만 쓰이고 실제 배포판에서는 사용되지 않을 것이라는 뜻입니다. `package.json`을 확인해보면 아까 설치한 jquery와는 다르게 `devDependencies`로 설치된 것을 볼 수 있습니다. 그럼 이제 webpack을 세팅해볼까요? webpack을 세팅하기 위해서는 `webpack.config.js`라는 파일을 만들어야 합니다. 파일 이름은 꼭 `webpack.config.js`이어야 webpack이 실행될 때 이 파일을 참조하게 됩니다. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' } }; `entry`는 번들링할 파일, `output`은 번들링한 결과물로 나올 파일을 의미합니다. 우리는 `index.js`를 번들링해서 `bundle.js`로 내보낼 것입니다. `index.html`을 수정합시다. <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1>Hello, world!</h1> <script src="bundle.js"></script> </body> </html> 그리고 webpack을 실행하면! $ ./node_modules/.bin/webpack `bundle.js`가 만들어지고, 사이트도 잘 돌아갑니다. 하지만 매번 `index.js`를 수정할 때마다 webpack을 실행해주는 건 불편합니다. 이건 어떡할까요? # task runner 사용하기 task runner는 빌드를 자동화해주는 도구입니다. 예전에는 [Grunt](https://gruntjs.com/)나 [Gulp](https://gulpjs.com/)가 쓰였지만, 요즘은 npm에 포함되어 있는 스크립트 기능을 사용하는 추세입니다. 별도의 패키지를 설치할 필요 없이 `package.json`만 수정하면 바로 되니까 정말 편합니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", }, "dependencies": { "jquery": "^3.3.1" } } `build`와 `watch`라는 명령어를 추가했습니다. $ npm run build `build`를 하면 webpack이 실행되며 코드를 빌드해줍니다. UglifyJS와 같은 플러그인을 설치하고 스크립트를 조금 수정하면 minify된 배포용 코드를 만들 수도 있습니다. 이건 다음 글에서 다루겠습니다. $ npm run watch `watch`는 자바스크립트 파일이 수정될 때마다 자동으로 webpack을 실행시켜줍니다. 한 번만 켜두면 자바스크립트 코드를 수정하고 저장할 때마다 webpack이 실행되어 모든 코드가 번들링됩니다. 이제 여기에 웹 서버를 돌리면 개발환경을 더 편리하게 만들 수 있습니다. # 웹 서버 사용하기 아파치나 Nginx를 설치하지 않아도 웹서버를 구축할 수 있습니다. webpack이 제공하는 웹서버를 설치하면 됩니다. $ npm install webpack-dev-server --save-dev 설치가 끝나면 `package.json`에 스크립트를 추가해줍니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", "webpack-dev-server": "^2.11.1" }, "dependencies": { "jquery": "^3.3.1" } } `build`와 `watch`처럼 `server`를 사용할 수 있게 되었습니다. 그리고 `webpack.config.js`에도 서버에 대한 설정을 추가해줍니다. module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 8000 }, }; 그리고 서버를 실행하세요. $ npm run server 이렇게 하면 `webpack.config.js`가 있는 디렉토리의 `index.html`이 `localhost:8000`에서 열립니다. 자바스크립트 파일을 수정하면 브라우저를 새로고침해주기도 합니다. 정말 엄청나게 편해졌죠. 이제 기본적인 세팅을 모두 끝냈습니다. 이번 글에서 우리는 5개의 파일(`index.html`, `index.js`, `bundle.js`, `package.json`, `webpack.config.js`)과 하나의 폴더(`node_modules`)를 얻었습니다. **index.html** <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="bundle.js"></script> </body> </html> **index.js** var $ = require('jquery'); console.log($('#title').text()); **package.json** { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", "webpack-dev-server": "^2.11.1" }, "dependencies": { "jquery": "^3.3.1" } } **webpack.config.js** module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 8000 }, };
json metadata{"tags":["front-end","javascript","web","kr","font-end"],"image":["https://i.imgur.com/KzCHMAx.gif"],"links":["https://github.com/kamranahmedse/developer-roadmap","https://bower.io/","https://nodejs.org/ko/","http://browserify.org/","https://gruntjs.com/","https://gulpjs.com/"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfont-end
permlink21-1
title21세기 프론트엔드 (1) 시작하기
Transaction InfoBlock #19885017/Trx b695871d7ad5d12c2a9e40400ae122e812cfdd39
View Raw JSON Data
{
  "block": 19885017,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "![Modern software development](https://i.imgur.com/KzCHMAx.gif)\n\n솔직히 프론트엔드 개발에 대해 이 움짤보다 더 잘 설명할 자신이 없습니다. 당장 [2018년 웹 개발자 로드맵](https://github.com/kamranahmedse/developer-roadmap)만 봐도 배울게 넘칩니다. (그리고 지금 이 시간에도 계속해서 늘어나고 있습니다.) 이제 막 프론트엔드 개발을 시작하려는 분이나 오래전에 웹 개발을 배운 뒤 정보를 업데이트하지 않은 분들은 아득한 기분이 들겁니다. 그래도 막상 시작해보면 생각만큼 어렵지는 않습니다. 모든 것이 서로 얽혀있기 때문에 하나를 배우면 동시에 다른 하나를 같이 배우게 됩니다. 이번 글에서만 npm과 npm scripts, webpack을 다룹니다. 너무 걱정하지 마세요.\n\n# 오래된 방법\n\n처음 웹 프로그래밍을 배울 때는 HTML 파일에 외부 CSS나 자바스크립트 코드를 `<link>`태그나 `<script>`태그로 가져오라고 배웁니다. 이렇게요:\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"index.js\"></script>\n    </body>\n    </html>\n\n이정도만 해도 웹사이트는 잘 돌아갑니다. 하지만 많은 점이 불편합니다.\n\n*   만약 자바스크립트 코드가 방대해져서 파일을 30개로 쪼갠다면 `index.html`에는 30개의 script태그가 필요하게 됩니다. CSS도 마찬가지구요.\n*   뿐만 아니라 외부 자바스크립트 라이브러리를 가져올 때도 문제가 생깁니다. 만일 jQuery 3.3.1 버전을 다운받아서 `<script src=\"jquery-3.3.1.min.js\"></script>`와 같은 코드를 작성한다면 jQuery 3.4.0 버전이 나왔을 땐 다시 코드를 다운받는 수고를 해야합니다.\n*   다른 사람과 프로젝트를 공유할 때도 불편합니다. 어떤 파일이 어떤 라이브러리나 프레임워크를 사용하고 있는지 일일히 설명해야하고, 또 그 파일들을 모두 공유해야 하니까요.\n\n# npm(node package manager) 사용하기\n\n그래서 npm이라고 하는 '패키지 매니저'를 사용합니다. (원래는 대부분 [Bower](https://bower.io/)를 사용했는데, 이젠 npm이 더 인기있습니다.) npm은 마치 앱스토어에서 어플리케이션을 다운받아 쓰듯이 자바스크립트 패키지를 쉽게 찾고 다운받아서 웹사이트에 적용시킬 수 있도록 만들어줍니다. 이렇게 하면 해당 웹사이트가 어떤 패키지를 사용하고 있는지 한 눈에 볼 수 있고, 패키지의 버전 관리도 훨씬 쉬워집니다.\n\nnpm은 Node.js를 기반으로 합니다. [Node.js 홈페이지](https://nodejs.org/ko/)에서 안정적인 LTS버전을 다운받으세요. Node.js를 설치하면 npm도 설치됩니다. npm을 사용한다는 것은 CLI 환경에서 작업을 한다는 것을 의미합니다. 많은 분들이 커맨드 라인을 보면 겁을 먹는데, 너무 걱정하지 않으셔도 됩니다. 익숙해지면 오히려 GUI보다 편합니다. 자, 설치가 끝났다면 이제 터미널이나 cmd를 열고 잘 설치되었는지 확인해보세요.\n\nNode.js 버전 확인:\n\n    $ node -v\n\nnpm 버전 확인:\n\n    $ npm -v\n\n이제 21세기 프론트엔드 개발 환경 세팅에 첫 삽을 떴습니다. `index.html`이 있는 작업 디렉토리로 이동해서 npm 환경을 초기화해줍니다.\n\n    $ npm init\n\n이것저것 설정하는 질문이 뜰텐데 그냥 모두 엔터를 눌러주세요. 나중에 수정할 수 있습니다. 설정이 끝났다면 `index.html`이 있는 디렉토리에 `package.json`이라는 새로운 파일이 생깁니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\"\n    }\n\n이제 npm으로 jQuery를 설치해볼까요?\n\n    $ npm install jquery\n\n이 명령어를 실행하면 npm의 중앙 저장소에서 jQuery를 다운받아 `node_modules`라는 폴더에 저장합니다. `package.json`을 새로고침하면 내용이 바뀐 것을 볼 수 있습니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n정말 편합니다. 이제 다른 사람과 프로젝트를 함께 할 때 `node_modules` 폴더를 주지 않고 `package.json` 파일을 주면 상대방은 `$ npm install` 명령어를 통해 필요한 자바스크립트 패키지를 한번에 다운받을 수 있게 됩니다. `node_modules/jquery/dist/`폴더를 보면 jQuery 코드들이 모여있는 것을 볼 수 있습니다.\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"node_modules/jquery/dist/jquery.min.js\"></script>\n      <script src=\"index.js\"></script>\n    </body>\n    </html>\n\n이렇게 하면 `index.js`에서 jQuery를 사용할 수 있습니다. 하지만 패키지를 설치하고 불러 올때마다 `node_moduels` 폴더를 뒤져봐야 하는 것은 여전히 불편합니다. 하지만 이것도 쉽게 해결할 수 있습니다.\n\n    // index.js\n    var $ = require('jquery');\n    console.log($('#title').text());\n\n`jquery`는 전역에서 사용 가능하며, Node.js는 서버사이드 언어이기 때문에 패키지의 경로를 따로 지정해주지 않아도 모두 알아서 패키지를 가져와 줍니다. `index.html`에서 jQuery를 불러오는 `<script>`태그를 지우고 사이트를 새로고침해보세요. 어떤가요? 아쉽지만 브라우저는 `require`를 모르기 때문에 에러가 뜹니다. (브라우저는 파일 시스템에 접근할 권한을 가지고 있지 않습니다.) 그래서 우리는 webpack를 사용합니다.\n\n# webpack 사용하기\n\nwebpack은 '모듈 번들러'입니다. [Browserify](http://browserify.org/)라는 모듈 번들러도 있는데, webpack은 번들링 뿐만 아니라 부가적인 기능을 많이 제공하고 있어 매우 편리합니다. 번들링이라는 것은 여러 자바스크립트 파일들을 하나로 합친다는 의미입니다. 현재 `index.html`에서는 `jquery.min.js`와 `index.js`를 따로 불러들이고 있는데, webpack을 사용하면 `bundle.js`와 같은 파일 하나만 부를 수 있게 됩니다. 또한 앞서 사용하지 못한 `require`도 사용할 수 있게 됩니다. 그럼 webpack을 설치해봅니다.\n\n    $ npm install webpack --save-dev\n\n`--save-dev`은 개발 의존으로 설치하겠다는 의미입니다. 즉, webpack이 개발용으로만 쓰이고 실제 배포판에서는 사용되지 않을 것이라는 뜻입니다. `package.json`을 확인해보면 아까 설치한 jquery와는 다르게 `devDependencies`로 설치된 것을 볼 수 있습니다.\n\n그럼 이제 webpack을 세팅해볼까요? webpack을 세팅하기 위해서는 `webpack.config.js`라는 파일을 만들어야 합니다. 파일 이름은 꼭 `webpack.config.js`이어야 webpack이 실행될 때 이 파일을 참조하게 됩니다.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      }\n    };\n\n`entry`는 번들링할 파일, `output`은 번들링한 결과물로 나올 파일을 의미합니다. 우리는 `index.js`를 번들링해서 `bundle.js`로 내보낼 것입니다. `index.html`을 수정합시다.\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1>Hello, world!</h1>\n      <script src=\"bundle.js\"></script>\n    </body>\n    </html>\n\n그리고 webpack을 실행하면!\n\n    $ ./node_modules/.bin/webpack\n\n`bundle.js`가 만들어지고, 사이트도 잘 돌아갑니다. 하지만 매번 `index.js`를 수정할 때마다 webpack을 실행해주는 건 불편합니다. 이건 어떡할까요?\n\n# task runner 사용하기\n\ntask runner는 빌드를 자동화해주는 도구입니다. 예전에는 [Grunt](https://gruntjs.com/)나 [Gulp](https://gulpjs.com/)가 쓰였지만, 요즘은 npm에 포함되어 있는 스크립트 기능을 사용하는 추세입니다. 별도의 패키지를 설치할 필요 없이 `package.json`만 수정하면 바로 되니까 정말 편합니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n`build`와 `watch`라는 명령어를 추가했습니다.\n\n    $ npm run build\n\n`build`를 하면 webpack이 실행되며 코드를 빌드해줍니다. UglifyJS와 같은 플러그인을 설치하고 스크립트를 조금 수정하면 minify된 배포용 코드를 만들 수도 있습니다. 이건 다음 글에서 다루겠습니다.\n\n    $ npm run watch\n\n`watch`는 자바스크립트 파일이 수정될 때마다 자동으로 webpack을 실행시켜줍니다. 한 번만 켜두면 자바스크립트 코드를 수정하고 저장할 때마다 webpack이 실행되어 모든 코드가 번들링됩니다. 이제 여기에 웹 서버를 돌리면 개발환경을 더 편리하게 만들 수 있습니다.\n\n# 웹 서버 사용하기\n\n아파치나 Nginx를 설치하지 않아도 웹서버를 구축할 수 있습니다. webpack이 제공하는 웹서버를 설치하면 됩니다.\n\n    $ npm install webpack-dev-server --save-dev\n\n설치가 끝나면 `package.json`에 스크립트를 추가해줍니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\",\n        \"server\": \"webpack-dev-server --open\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n        \"webpack-dev-server\": \"^2.11.1\"\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n`build`와 `watch`처럼 `server`를 사용할 수 있게 되었습니다. 그리고 `webpack.config.js`에도 서버에 대한 설정을 추가해줍니다.\n\n        module.exports = {\n          entry: './index.js',\n          output: {\n            filename: 'bundle.js'\n          },\n          devServer: {\n            contentBase: './',\n            compress: true,\n            port: 8000\n          },\n        };\n\n그리고 서버를 실행하세요.\n\n    $ npm run server\n\n이렇게 하면 `webpack.config.js`가 있는 디렉토리의 `index.html`이 `localhost:8000`에서 열립니다. 자바스크립트 파일을 수정하면 브라우저를 새로고침해주기도 합니다. 정말 엄청나게 편해졌죠. 이제 기본적인 세팅을 모두 끝냈습니다. 이번 글에서 우리는 5개의 파일(`index.html`, `index.js`, `bundle.js`, `package.json`, `webpack.config.js`)과 하나의 폴더(`node_modules`)를 얻었습니다.\n\n**index.html**\n\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"bundle.js\"></script>\n    </body>\n    </html>\n\n**index.js**\n\n    var $ = require('jquery');\n    console.log($('#title').text());\n\n**package.json**\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\",\n        \"server\": \"webpack-dev-server --open\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n        \"webpack-dev-server\": \"^2.11.1\"\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n**webpack.config.js**\n\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 8000\n      },\n    };",
      "json_metadata": "{\"tags\":[\"front-end\",\"javascript\",\"web\",\"kr\",\"font-end\"],\"image\":[\"https://i.imgur.com/KzCHMAx.gif\"],\"links\":[\"https://github.com/kamranahmedse/developer-roadmap\",\"https://bower.io/\",\"https://nodejs.org/ko/\",\"http://browserify.org/\",\"https://gruntjs.com/\",\"https://gulpjs.com/\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "font-end",
      "permlink": "21-1",
      "title": "21세기 프론트엔드 (1) 시작하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T07:24:54",
  "trx_id": "b695871d7ad5d12c2a9e40400ae122e812cfdd39",
  "trx_in_block": 23,
  "virtual_op": 0
}
caesium133published a new post: 21-2-webpack
2018/02/15 07:24:12
authorcaesium133
body[21세기 프론트엔드 (1) 시작하기](https://steemit.com/font-end/@caesium133/21-1)에서 npm과 webpack을 설치하고 기본적인 개발 환경을 세팅하는 작업을 했습니다. 이번 글에서는 webpack을 더 확장해서 자바스크립트 뿐만 아니라 CSS도 함께 번들링할 수 있도록 만들고, 더불어 개발환경에서 ES6를 사용하기 위해 [Babel](https://babeljs.io/)을 설치해보겠습니다. 여기까지 "무슨 말인지 하나도 모르겠다!"하셔도 괜찮습니다. # CSS 번들링하기 이전 글에서 `index.js`와 jQuery를 번들링해서 `index.html`에서는 `bundle.js`만을 불러들이도록 했는데요, 여기에 CSS 파일도 추가해보려 합니다. /* index.css */ h1 { color: #ff0000; } 옛날 방법대로라면 `index.html`의 `<head>`태그 안에 `<style rel="stylesheet" href="index.css" />`와 같은 코드를 넣었겠지만, 이건 깔끔하지 않습니다. webpack을 활용해봅시다. 먼저 두 개의 로더를 설치해주세요. $ npm install css-loader style-loader --save-dev 둘 다 개발 환경에서만 사용할 것이기 때문에 개발 의존성으로 설치했습니다. 로더는 각 모듈을 특정 규칙에 따라 가공해줍니다. 로더를 설치했으니 `webpack.config.js`를 수정해주세요. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.css$/, loader: 'style-loader!css-loader' } ] } }; 이제 정말 간단하게 CSS 파일을 번들링할 수 있습니다. 로더에 `style-loader`와 `css-loader`를 추가함으로써 webpack이 외부 CSS 파일을 읽고, 이것을 HTML 파일의 `<head>`태그에 `<style>`태그를 넣는 방식으로 CSS를 번들링할 수 있게 되었습니다. `index.js` 파일을 수정해보세요. // index.js var $ = require('jquery'); var style = require('./index.css'); console.log($('h1').text()); 이제 `npm run build`를 통해 webpack을 실행하면 `index.html`에 CSS가 적용된 것을 볼 수 있습니다. (`npm run watch`를 해두셨다면 `index.js`를 저장하자마자 자동으로 webpack이 실행됩니다.) # Babel 사용하기 Babel은 트랜스파일러(Transpiler)입니다. 트랜스파일러는 ES6로 작성된 자바스크립트 코드를 브라우저와 호환되는 버전으로 바꿔줍니다. (브라우저가 자바스크립트 버전을 바로 따라가지 못하는 경우가 있기 때문에 트랜스파일러가 필요합니다.) 즉, 개발 환경에서 ES6의 기능들을 사용해도 괜찮다는 뜻입니다. 과거에는 [CoffeeScript](http://coffeescript.org/)가 주로 사용됐습니다. 만약 ES6가 무엇인지 잘 모르시는 분들은 [ES6시대의 JavaScript](https://gist.github.com/marocchino/841e2ff62f59f420f9d9) 또는 [지금 바로 시작하는 ES6](http://meetup.toast.com/posts/85)를 읽어보세요. npm을 통해 Bable 패키지를 설치해야 합니다. $ npm install babel-core babel-preset-env babel-loader --save-dev `babel-core`와 `babel-preset-env`, `babel-loader` 세가지 패키지를 설치했습니다. `babel-core`와 `babel-preset-env`은 Babel의 핵심부이며, 자바스크립트의 어떤 기능을 트랜스파일할지 정의해놓은 패키지입니다. `babel-loader`는 앞서 설치한 `css-loader`와 `style-loader` 같은 로더입니다. `webpack.config.js`에서 로더를 추가해봅시다. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] } }; 이제 webpack이 실행될 때마다 `babel-loader`를 거치게 됩니다. `index.js`를 수정해서 ES6의 기능이 제대로 동작하는지 보죠. `import`는 `require`를 대체하는 ES6 문법입니다. 또한 `let`은 `var`와 달리 블록스코프를 갖는 타입으로 역시 ES6에서 추가되었습니다. 그리고 [템플릿 문자열](http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/)도 사용할 수 있습니다. // index.js import $ from 'jquery'; import './index.css'; let name = 'Park' console.log(`My name is ${name}`); console.log($('#title').text()); 좋습니다! 이제 배포 코드를 만드는 일만 남았네요. 이 부분은 다음 글에서 설명하도록 하겠습니다.
json metadata{"tags":["front-end","javascript","web","kr","font-end"],"links":["https://steemit.com/font-end/@caesium133/21-1","https://babeljs.io/","http://coffeescript.org/","https://gist.github.com/marocchino/841e2ff62f59f420f9d9","http://meetup.toast.com/posts/85","http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfont-end
permlink21-2-webpack
title21세기 프론트엔드 (2) webpack 확장하기
Transaction InfoBlock #19885003/Trx 672b54ad43063d02fed45482e3193de67d1c6309
View Raw JSON Data
{
  "block": 19885003,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "[21세기 프론트엔드 (1) 시작하기](https://steemit.com/font-end/@caesium133/21-1)에서 npm과 webpack을 설치하고 기본적인 개발 환경을 세팅하는 작업을 했습니다. 이번 글에서는 webpack을 더 확장해서 자바스크립트 뿐만 아니라 CSS도 함께 번들링할 수 있도록 만들고, 더불어 개발환경에서 ES6를 사용하기 위해 [Babel](https://babeljs.io/)을 설치해보겠습니다. 여기까지 \"무슨 말인지 하나도 모르겠다!\"하셔도 괜찮습니다.\n\n# CSS 번들링하기\n\n이전 글에서 `index.js`와 jQuery를 번들링해서 `index.html`에서는 `bundle.js`만을 불러들이도록 했는데요, 여기에 CSS 파일도 추가해보려 합니다.\n\n    /* index.css */\n    h1 {\n      color: #ff0000;\n    }\n\n옛날 방법대로라면 `index.html`의 `<head>`태그 안에 `<style rel=\"stylesheet\" href=\"index.css\" />`와 같은 코드를 넣었겠지만, 이건 깔끔하지 않습니다. webpack을 활용해봅시다. 먼저 두 개의 로더를 설치해주세요.\n\n    $ npm install css-loader style-loader --save-dev\n\n둘 다 개발 환경에서만 사용할 것이기 때문에 개발 의존성으로 설치했습니다. 로더는 각 모듈을 특정 규칙에 따라 가공해줍니다. 로더를 설치했으니 `webpack.config.js`를 수정해주세요.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [ \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      }\n    };\n\n이제 정말 간단하게 CSS 파일을 번들링할 수 있습니다. 로더에 `style-loader`와 `css-loader`를 추가함으로써 webpack이 외부 CSS 파일을 읽고, 이것을 HTML 파일의 `<head>`태그에 `<style>`태그를 넣는 방식으로 CSS를 번들링할 수 있게 되었습니다. `index.js` 파일을 수정해보세요.\n\n    // index.js\n    var $ = require('jquery');\n    var style = require('./index.css');\n    console.log($('h1').text());\n\n이제 `npm run build`를 통해 webpack을 실행하면 `index.html`에 CSS가 적용된 것을 볼 수 있습니다. (`npm run watch`를 해두셨다면 `index.js`를 저장하자마자 자동으로 webpack이 실행됩니다.)\n\n# Babel 사용하기\n\nBabel은 트랜스파일러(Transpiler)입니다. 트랜스파일러는 ES6로 작성된 자바스크립트 코드를 브라우저와 호환되는 버전으로 바꿔줍니다. (브라우저가 자바스크립트 버전을 바로 따라가지 못하는 경우가 있기 때문에 트랜스파일러가 필요합니다.) 즉, 개발 환경에서 ES6의 기능들을 사용해도 괜찮다는 뜻입니다. 과거에는 [CoffeeScript](http://coffeescript.org/)가 주로 사용됐습니다. 만약 ES6가 무엇인지 잘 모르시는 분들은 [ES6시대의 JavaScript](https://gist.github.com/marocchino/841e2ff62f59f420f9d9) 또는 [지금 바로 시작하는 ES6](http://meetup.toast.com/posts/85)를 읽어보세요.\n\nnpm을 통해 Bable 패키지를 설치해야 합니다.\n\n    $ npm install babel-core babel-preset-env babel-loader --save-dev\n\n`babel-core`와 `babel-preset-env`, `babel-loader` 세가지 패키지를 설치했습니다. `babel-core`와 `babel-preset-env`은 Babel의 핵심부이며, 자바스크립트의 어떤 기능을 트랜스파일할지 정의해놓은 패키지입니다. `babel-loader`는 앞서 설치한 `css-loader`와 `style-loader` 같은 로더입니다. `webpack.config.js`에서 로더를 추가해봅시다.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n            use: {\n              loader: 'babel-loader',\n              options: {\n                presets: ['env']\n              }\n            }\n          }, \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      }\n    };\n\n이제 webpack이 실행될 때마다 `babel-loader`를 거치게 됩니다. `index.js`를 수정해서 ES6의 기능이 제대로 동작하는지 보죠. `import`는 `require`를 대체하는 ES6 문법입니다. 또한 `let`은 `var`와 달리 블록스코프를 갖는 타입으로 역시 ES6에서 추가되었습니다. 그리고 [템플릿 문자열](http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/)도 사용할 수 있습니다.\n\n    // index.js\n    import $ from 'jquery';\n    import './index.css';\n\n    let name = 'Park'\n\n    console.log(`My name is ${name}`);\n    console.log($('#title').text());\n\n좋습니다! 이제 배포 코드를 만드는 일만 남았네요. 이 부분은 다음 글에서 설명하도록 하겠습니다.",
      "json_metadata": "{\"tags\":[\"front-end\",\"javascript\",\"web\",\"kr\",\"font-end\"],\"links\":[\"https://steemit.com/font-end/@caesium133/21-1\",\"https://babeljs.io/\",\"http://coffeescript.org/\",\"https://gist.github.com/marocchino/841e2ff62f59f420f9d9\",\"http://meetup.toast.com/posts/85\",\"http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "font-end",
      "permlink": "21-2-webpack",
      "title": "21세기 프론트엔드 (2) webpack 확장하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T07:24:12",
  "trx_id": "672b54ad43063d02fed45482e3193de67d1c6309",
  "trx_in_block": 14,
  "virtual_op": 0
}
caesium133published a new post: 21-2-webpack
2018/02/15 07:23:57
authorcaesium133
body[21세기 프론트엔드 (1) 시작하기](https://steemit.com/font-end/@caesium133/21-1)에서 npm과 webpack을 설치하고 기본적인 개발 환경을 세팅하는 작업을 했습니다. 이번 글에서는 webpack을 더 확장해서 자바스크립트 뿐만 아니라 CSS도 함께 번들링할 수 있도록 만들고, 더불어 개발환경에서 ES6를 사용하기 위해 [Babel](https://babeljs.io/)을 설치해보겠습니다. 여기까지 "무슨 말인지 하나도 모르겠다!"하셔도 괜찮습니다. # CSS 번들링하기 이전 글에서 `index.js`와 jQuery를 번들링해서 `index.html`에서는 `bundle.js`만을 불러들이도록 했는데요, 여기에 CSS 파일도 추가해보려 합니다. /* index.css */ h1 { color: #ff0000; } 옛날 방법대로라면 `index.html`의 `<head>`태그 안에 `<style rel="stylesheet" href="index.css" />`와 같은 코드를 넣었겠지만, 이건 깔끔하지 않습니다. webpack을 활용해봅시다. 먼저 두 개의 로더를 설치해주세요. $ npm install css-loader style-loader --save-dev 둘 다 개발 환경에서만 사용할 것이기 때문에 개발 의존성으로 설치했습니다. 로더는 각 모듈을 특정 규칙에 따라 가공해줍니다. 로더를 설치했으니 `webpack.config.js`를 수정해주세요. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.css$/, loader: 'style-loader!css-loader' } ] } }; 이제 정말 간단하게 CSS 파일을 번들링할 수 있습니다. 로더에 `style-loader`와 `css-loader`를 추가함으로써 webpack이 외부 CSS 파일을 읽고, 이것을 HTML 파일의 `<head>`태그에 `<style>`태그를 넣는 방식으로 CSS를 번들링할 수 있게 되었습니다. `index.js` 파일을 수정해보세요. // index.js var $ = require('jquery'); var style = require('./index.css'); console.log($('h1').text()); 이제 `npm run build`를 통해 webpack을 실행하면 `index.html`에 CSS가 적용된 것을 볼 수 있습니다. (`npm run watch`를 해두셨다면 `index.js`를 저장하자마자 자동으로 webpack이 실행됩니다.) # Babel 사용하기 Babel은 트랜스파일러(Transpiler)입니다. 트랜스파일러는 ES6로 작성된 자바스크립트 코드를 브라우저와 호환되는 버전으로 바꿔줍니다. (브라우저가 자바스크립트 버전을 바로 따라가지 못하는 경우가 있기 때문에 트랜스파일러가 필요합니다.) 즉, 개발 환경에서 ES6의 기능들을 사용해도 괜찮다는 뜻입니다. 과거에는 [CoffeeScript](http://coffeescript.org/)가 주로 사용됐습니다. 만약 ES6가 무엇인지 잘 모르시는 분들은 [ES6시대의 JavaScript](https://gist.github.com/marocchino/841e2ff62f59f420f9d9) 또는 [지금 바로 시작하는 ES6](http://meetup.toast.com/posts/85)를 읽어보세요. npm을 통해 Bable 패키지를 설치해야 합니다. $ npm install babel-core babel-preset-env babel-loader --save-dev `babel-core`와 `babel-preset-env`, `babel-loader` 세가지 패키지를 설치했습니다. `babel-core`와 `babel-preset-env`은 Babel의 핵심부이며, 자바스크립트의 어떤 기능을 트랜스파일할지 정의해놓은 패키지입니다. `babel-loader`는 앞서 설치한 `css-loader`와 `style-loader` 같은 로더입니다. `webpack.config.js`에서 로더를 추가해봅시다. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] } }; 이제 webpack이 실행될 때마다 `babel-loader`를 거치게 됩니다. `index.js`를 수정해서 ES6의 기능이 제대로 동작하는지 보죠. `import`는 `require`를 대체하는 ES6 문법입니다. 또한 `let`은 `var`와 달리 블록스코프를 갖는 타입으로 역시 ES6에서 추가되었습니다. 그리고 [템플릿 문자열](http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/)도 사용할 수 있습니다. // index.js import $ from 'jquery'; import './index.css'; let name = 'Park' console.log(`My name is ${name}`); console.log($('#title').text()); 좋습니다! 이제 배포 코드를 만드는 일만 남았네요. 이 부분은 다음 글에서 설명하도록 하겠습니다.
json metadata{"tags":["front-end","javascript","web","kr","font-end"],"links":["https://steemit.com/font-end/@caesium133/21-1","https://babeljs.io/","http://coffeescript.org/","https://gist.github.com/marocchino/841e2ff62f59f420f9d9","http://meetup.toast.com/posts/85","http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfont-end
permlink21-2-webpack
title21세기 프론트엔드 (2) webpack 확장하기
Transaction InfoBlock #19884998/Trx 01092c8cb0a21a1d03af3da5ec4279047a445fcb
View Raw JSON Data
{
  "block": 19884998,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "[21세기 프론트엔드 (1) 시작하기](https://steemit.com/font-end/@caesium133/21-1)에서 npm과 webpack을 설치하고 기본적인 개발 환경을 세팅하는 작업을 했습니다. 이번 글에서는 webpack을 더 확장해서 자바스크립트 뿐만 아니라 CSS도 함께 번들링할 수 있도록 만들고, 더불어 개발환경에서 ES6를 사용하기 위해 [Babel](https://babeljs.io/)을 설치해보겠습니다. 여기까지 \"무슨 말인지 하나도 모르겠다!\"하셔도 괜찮습니다.\n\n# CSS 번들링하기\n\n이전 글에서 `index.js`와 jQuery를 번들링해서 `index.html`에서는 `bundle.js`만을 불러들이도록 했는데요, 여기에 CSS 파일도 추가해보려 합니다.\n\n    /* index.css */\n    h1 {\n      color: #ff0000;\n    }\n\n옛날 방법대로라면 `index.html`의 `<head>`태그 안에 `<style rel=\"stylesheet\" href=\"index.css\" />`와 같은 코드를 넣었겠지만, 이건 깔끔하지 않습니다. webpack을 활용해봅시다. 먼저 두 개의 로더를 설치해주세요.\n\n    $ npm install css-loader style-loader --save-dev\n\n둘 다 개발 환경에서만 사용할 것이기 때문에 개발 의존성으로 설치했습니다. 로더는 각 모듈을 특정 규칙에 따라 가공해줍니다. 로더를 설치했으니 `webpack.config.js`를 수정해주세요.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [ \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      }\n    };\n\n이제 정말 간단하게 CSS 파일을 번들링할 수 있습니다. 로더에 `style-loader`와 `css-loader`를 추가함으로써 webpack이 외부 CSS 파일을 읽고, 이것을 HTML 파일의 `<head>`태그에 `<style>`태그를 넣는 방식으로 CSS를 번들링할 수 있게 되었습니다. `index.js` 파일을 수정해보세요.\n\n    // index.js\n    var $ = require('jquery');\n    var style = require('./index.css');\n    console.log($('h1').text());\n\n이제 `npm run build`를 통해 webpack을 실행하면 `index.html`에 CSS가 적용된 것을 볼 수 있습니다. (`npm run watch`를 해두셨다면 `index.js`를 저장하자마자 자동으로 webpack이 실행됩니다.)\n\n# Babel 사용하기\n\nBabel은 트랜스파일러(Transpiler)입니다. 트랜스파일러는 ES6로 작성된 자바스크립트 코드를 브라우저와 호환되는 버전으로 바꿔줍니다. (브라우저가 자바스크립트 버전을 바로 따라가지 못하는 경우가 있기 때문에 트랜스파일러가 필요합니다.) 즉, 개발 환경에서 ES6의 기능들을 사용해도 괜찮다는 뜻입니다. 과거에는 [CoffeeScript](http://coffeescript.org/)가 주로 사용됐습니다. 만약 ES6가 무엇인지 잘 모르시는 분들은 [ES6시대의 JavaScript](https://gist.github.com/marocchino/841e2ff62f59f420f9d9) 또는 [지금 바로 시작하는 ES6](http://meetup.toast.com/posts/85)를 읽어보세요.\n\nnpm을 통해 Bable 패키지를 설치해야 합니다.\n\n    $ npm install babel-core babel-preset-env babel-loader --save-dev\n\n`babel-core`와 `babel-preset-env`, `babel-loader` 세가지 패키지를 설치했습니다. `babel-core`와 `babel-preset-env`은 Babel의 핵심부이며, 자바스크립트의 어떤 기능을 트랜스파일할지 정의해놓은 패키지입니다. `babel-loader`는 앞서 설치한 `css-loader`와 `style-loader` 같은 로더입니다. `webpack.config.js`에서 로더를 추가해봅시다.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n            use: {\n              loader: 'babel-loader',\n              options: {\n                presets: ['env']\n              }\n            }\n          }, \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      }\n    };\n\n이제 webpack이 실행될 때마다 `babel-loader`를 거치게 됩니다. `index.js`를 수정해서 ES6의 기능이 제대로 동작하는지 보죠. `import`는 `require`를 대체하는 ES6 문법입니다. 또한 `let`은 `var`와 달리 블록스코프를 갖는 타입으로 역시 ES6에서 추가되었습니다. 그리고 [템플릿 문자열](http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/)도 사용할 수 있습니다.\n\n    // index.js\n    import $ from 'jquery';\n    import './index.css';\n\n    let name = 'Park'\n\n    console.log(`My name is ${name}`);\n    console.log($('#title').text());\n\n좋습니다! 이제 배포 코드를 만드는 일만 남았네요. 이 부분은 다음 글에서 설명하도록 하겠습니다.",
      "json_metadata": "{\"tags\":[\"front-end\",\"javascript\",\"web\",\"kr\",\"font-end\"],\"links\":[\"https://steemit.com/font-end/@caesium133/21-1\",\"https://babeljs.io/\",\"http://coffeescript.org/\",\"https://gist.github.com/marocchino/841e2ff62f59f420f9d9\",\"http://meetup.toast.com/posts/85\",\"http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "font-end",
      "permlink": "21-2-webpack",
      "title": "21세기 프론트엔드 (2) webpack 확장하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T07:23:57",
  "trx_id": "01092c8cb0a21a1d03af3da5ec4279047a445fcb",
  "trx_in_block": 12,
  "virtual_op": 0
}
caesium133published a new post: 21-3
2018/02/15 07:23:06
authorcaesium133
body[이전 글](https://steemit.com/font-end/@caesium133/21-2-webpack)에서 기본적인 개발환경을 세팅했습니다. 이제 프로그램을 완성했다고 가정하고, 완성된 코드와 디렉토리를 서버에 올릴 수 있는 배포용으로 가공하는 방법을 살펴보겠습니다. # UglifyJS 사용하기 코드가 길어질 경우 번들링된 `bundle.js` 파일이 엄청나게 커질 수 있습니다. 그래서 코드를 압축하는 과정이 필요한데, 이때 사용하는 것이 UglifyJS입니다. 이와 같은 압축기(compressor)에는 [YUI Compressor](http://yui.github.io/yuicompressor/)나 [Google Closure Compiler](https://closure-compiler.appspot.com/home)가 있습니다. 이러한 압축기는 코드의 변수명도 모두 한 글자 정도로 바꿔주기 때문에 압축과 난독화의 기능을 동시에 합니다. 우리는 webpack의 플러그인으로 만들어진 UglifyJS를 사용할 것입니다. npm을 통해 설치해주세요. npm install uglifyjs-webpack-plugin --save-dev 그리고 `webpack.config.js`을 수정해 이 플러그인을 적용해줍니다. // webpack.config.js const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] }, plugins: [ new UglifyJsPlugin() ], }; 자, 이제 터미널에서 `npm run build`를 실행하면 UglifyJS를 거쳐 압축된 번들 코드가 만들어지는 것을 볼 수 있습니다! # CopyWebpackPlugin 사용하기 지금까지 하나의 폴더에서 모든 파일을 관리해왔는데요, 실제로 프로젝트의 규모가 커지면 단일 폴더에서 파일을 관리하기는 쉽지 않습니다. 그래서 아래와 같이 폴더를 쪼개개됩니다. . ├── dist/ │ └── index.html │ └── css/ │ └── index.css │ └── js/ │ └── bundle.js ├── public/ │ └── index.html │ └── css/ │ └── index.css ├── src/ │ └── index.js ├── node_modules/ │ └── ... ├── package.json └── webpack.config.js 이건 제가 주로 사용하는 디렉토리 구조이고, 사용하는 프레임워크나 언어, 환경에 따라 디렉토리 구조는 바뀔 수 있습니다. (Vue.js의 경우 [이런 식](http://vuejs-templates.github.io/webpack/structure.html)의 디렉토리 구조를 제안합니다.) dist는 배포파일을, public은 html/css와 같은 static 파일을, src는 자바스크립트 파일을 담는 폴더로 구성하고 있습니다. public이나 src 폴더는 개발환경에서 사용하고, 실제 서버에 올리거나 사용자에게 배포되는 코드는 dist에 모아둡니다. 물론 파일을 직접 손으로 옮기진 않고, CopyWebpackPlugin이라는 웹팩 플러그인을 사용합니다. npm install copy-webpack-plugin 플러그인을 설치한 뒤에는 역시 `webpack.config.js`를 수정해줍니다. // webpack.config.js const CopyWebpackPlugin = require('copy-webpack-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { entry: './index.js', output: { path: './dist/src/', filename: 'bundle.js' }, devServer: { contentBase: './dist/', compress: true, port: 9000 }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] }, plugins: [ new UglifyJsPlugin(), new CopyWebpackPlugin([{ from: './public/', to: './dist/' }]), ], }; `bundle.js`가 아웃풋되는 path를 바꿔서 dist 폴더 아래 src 폴더에 자동으로 넣도록했고, CopyWebpackPlugin은 public 폴더의 파일들을 dist 폴더에 넣도록 설정했습니다. 이제 빌드를 하면 자동으로 dist 폴더에 src 폴더, css 폴더, js 폴더가 생기면서 배포용 파일들을 모아주게 됩니다. devServer의 `contentBase` 값도 `./dist/`로 바꿔줍니다. 기본 튜토리얼은 끝났습니다! 앞으로는 ESLint나 Sass처럼 개발을 더욱 편리하게 만들어주는 도구들과 프레임워크들에 대해 다뤄보도록 하겠습니다.
json metadata{"tags":["front-end","development","javascript","web","kr"],"links":["https://steemit.com/font-end/@caesium133/21-2-webpack","http://yui.github.io/yuicompressor/","https://closure-compiler.appspot.com/home","http://vuejs-templates.github.io/webpack/structure.html"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfront-end
permlink21-3
title21세기 프론트엔드 (3) 배포하기
Transaction InfoBlock #19884981/Trx 4076a12dbab6c3d21a602c10f140bae0e080349c
View Raw JSON Data
{
  "block": 19884981,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "[이전 글](https://steemit.com/font-end/@caesium133/21-2-webpack)에서 기본적인 개발환경을 세팅했습니다. 이제 프로그램을 완성했다고 가정하고, 완성된 코드와 디렉토리를 서버에 올릴 수 있는 배포용으로 가공하는 방법을 살펴보겠습니다.\n\n# UglifyJS 사용하기\n\n코드가 길어질 경우 번들링된 `bundle.js` 파일이 엄청나게 커질 수 있습니다. 그래서 코드를 압축하는 과정이 필요한데, 이때 사용하는 것이 UglifyJS입니다. 이와 같은 압축기(compressor)에는 [YUI Compressor](http://yui.github.io/yuicompressor/)나 [Google Closure Compiler](https://closure-compiler.appspot.com/home)가 있습니다. 이러한 압축기는 코드의 변수명도 모두 한 글자 정도로 바꿔주기 때문에 압축과 난독화의 기능을 동시에 합니다. 우리는 webpack의 플러그인으로 만들어진 UglifyJS를 사용할 것입니다. npm을 통해 설치해주세요.\n\n    npm install uglifyjs-webpack-plugin --save-dev\n\n그리고 `webpack.config.js`을 수정해 이 플러그인을 적용해줍니다.\n\n    // webpack.config.js\n    const UglifyJsPlugin = require('uglifyjs-webpack-plugin');\n\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n            use: {\n              loader: 'babel-loader',\n              options: {\n                presets: ['env']\n              }\n            }\n          }, \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      },\n      plugins: [\n        new UglifyJsPlugin()\n      ],\n    };\n\n자, 이제 터미널에서 `npm run build`를 실행하면 UglifyJS를 거쳐 압축된 번들 코드가 만들어지는 것을 볼 수 있습니다!\n\n# CopyWebpackPlugin 사용하기\n\n지금까지 하나의 폴더에서 모든 파일을 관리해왔는데요, 실제로 프로젝트의 규모가 커지면 단일 폴더에서 파일을 관리하기는 쉽지 않습니다. 그래서 아래와 같이 폴더를 쪼개개됩니다.\n\n    .\n    ├── dist/\n    │   └── index.html\n    │   └── css/\n    │       └── index.css\n    │   └── js/\n    │       └── bundle.js\n    ├── public/\n    │   └── index.html\n    │   └── css/\n    │       └── index.css\n    ├── src/\n    │   └── index.js\n    ├── node_modules/\n    │   └── ...\n    ├── package.json\n    └── webpack.config.js\n\n이건 제가 주로 사용하는 디렉토리 구조이고, 사용하는 프레임워크나 언어, 환경에 따라 디렉토리 구조는 바뀔 수 있습니다. (Vue.js의 경우 [이런 식](http://vuejs-templates.github.io/webpack/structure.html)의 디렉토리 구조를 제안합니다.) dist는 배포파일을, public은 html/css와 같은 static 파일을, src는 자바스크립트 파일을 담는 폴더로 구성하고 있습니다. public이나 src 폴더는 개발환경에서 사용하고, 실제 서버에 올리거나 사용자에게 배포되는 코드는 dist에 모아둡니다. 물론 파일을 직접 손으로 옮기진 않고, CopyWebpackPlugin이라는 웹팩 플러그인을 사용합니다.\n\n    npm install copy-webpack-plugin\n\n플러그인을 설치한 뒤에는 역시 `webpack.config.js`를 수정해줍니다.\n\n    // webpack.config.js\n    const CopyWebpackPlugin = require('copy-webpack-plugin');\n    const UglifyJsPlugin = require('uglifyjs-webpack-plugin');\n\n    module.exports = {\n      entry: './index.js',\n      output: {\n        path: './dist/src/',\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './dist/',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n            use: {\n              loader: 'babel-loader',\n              options: {\n                presets: ['env']\n              }\n            }\n          }, \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      },\n      plugins: [\n        new UglifyJsPlugin(),\n        new CopyWebpackPlugin([{\n          from: './public/',\n          to: './dist/'\n        }]),\n      ],\n    };\n\n`bundle.js`가 아웃풋되는 path를 바꿔서 dist 폴더 아래 src 폴더에 자동으로 넣도록했고, CopyWebpackPlugin은 public 폴더의 파일들을 dist 폴더에 넣도록 설정했습니다. 이제 빌드를 하면 자동으로 dist 폴더에 src 폴더, css 폴더, js 폴더가 생기면서 배포용 파일들을 모아주게 됩니다. devServer의 `contentBase` 값도 `./dist/`로 바꿔줍니다.\n\n기본 튜토리얼은 끝났습니다! 앞으로는 ESLint나 Sass처럼 개발을 더욱 편리하게 만들어주는 도구들과 프레임워크들에 대해 다뤄보도록 하겠습니다.",
      "json_metadata": "{\"tags\":[\"front-end\",\"development\",\"javascript\",\"web\",\"kr\"],\"links\":[\"https://steemit.com/font-end/@caesium133/21-2-webpack\",\"http://yui.github.io/yuicompressor/\",\"https://closure-compiler.appspot.com/home\",\"http://vuejs-templates.github.io/webpack/structure.html\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "front-end",
      "permlink": "21-3",
      "title": "21세기 프론트엔드 (3) 배포하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T07:23:06",
  "trx_id": "4076a12dbab6c3d21a602c10f140bae0e080349c",
  "trx_in_block": 23,
  "virtual_op": 0
}
drgkimupvoted (100.00%) @caesium133 / 21-1
2018/02/15 02:29:45
authorcaesium133
permlink21-1
voterdrgkim
weight10000 (100.00%)
Transaction InfoBlock #19879116/Trx ccb6f1ee4f8c6100e911223d605ce681f46e2a89
View Raw JSON Data
{
  "block": 19879116,
  "op": [
    "vote",
    {
      "author": "caesium133",
      "permlink": "21-1",
      "voter": "drgkim",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-15T02:29:45",
  "trx_id": "ccb6f1ee4f8c6100e911223d605ce681f46e2a89",
  "trx_in_block": 9,
  "virtual_op": 0
}
caesium133published a new post: 21-2-webpack
2018/02/14 03:05:18
authorcaesium133
body[21세기 프론트엔드 (1) 시작하기](https://steemit.com/font-end/@caesium133/21-1)에서 npm과 webpack을 설치하고 기본적인 개발 환경을 세팅하는 작업을 했습니다. 이번 글에서는 webpack을 더 확장해서 자바스크립트 뿐만 아니라 CSS도 함께 번들링할 수 있도록 만들고, 더불어 개발환경에서 ES6를 사용하기 위해 [Babel](https://babeljs.io/)을 설치해보겠습니다. 여기까지 "무슨 말인지 하나도 모르겠다!"하셔도 괜찮습니다. # CSS 번들링하기 이전 글에서 `index.js`와 jQuery를 번들링해서 `index.html`에서는 `bundle.js`만을 불러들이도록 했는데요, 여기에 CSS 파일도 추가해보려 합니다. /* index.css */ h1 { color: #ff0000; } 옛날 방법대로라면 `index.html`의 `<head>`태그 안에 `<style rel="stylesheet" href="index.css" />`와 같은 코드를 넣었겠지만, 이건 깔끔하지 않습니다. webpack을 활용해봅시다. 먼저 두 개의 로더를 설치해주세요. $ npm install css-loader style-loader --save-dev 둘 다 개발 환경에서만 사용할 것이기 때문에 개발 의존성으로 설치했습니다. 로더는 각 모듈을 특정 규칙에 따라 가공해줍니다. 로더를 설치했으니 `webpack.config.js`를 수정해주세요. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.css$/, loader: 'style-loader!css-loader' } ] } }; 이제 정말 간단하게 CSS 파일을 번들링할 수 있습니다. 로더에 `style-loader`와 `css-loader`를 추가함으로써 webpack이 외부 CSS 파일을 읽고, 이것을 HTML 파일의 `<head>`태그에 `<style>`태그를 넣는 방식으로 CSS를 번들링할 수 있게 되었습니다. `index.js` 파일을 수정해보세요. // index.js var $ = require('jquery'); var style = require('./index.css'); console.log($('h1').text()); 이제 `npm run build`를 통해 webpack을 실행하면 `index.html`에 CSS가 적용된 것을 볼 수 있습니다. (`npm run watch`를 해두셨다면 `index.js`를 저장하자마자 자동으로 webpack이 실행됩니다.) # Babel 사용하기 Babel은 트랜스파일러(Transpiler)입니다. 트랜스파일러는 ES6로 작성된 자바스크립트 코드를 브라우저와 호환되는 버전으로 바꿔줍니다. (브라우저가 자바스크립트 버전을 바로 따라가지 못하는 경우가 있기 때문에 트랜스파일러가 필요합니다.) 즉, 개발 환경에서 ES6의 기능들을 사용해도 괜찮다는 뜻입니다. 과거에는 [CoffeeScript](http://coffeescript.org/)가 주로 사용됐습니다. 만약 ES6가 무엇인지 잘 모르시는 분들은 [ES6시대의 JavaScript](https://gist.github.com/marocchino/841e2ff62f59f420f9d9) 또는 [지금 바로 시작하는 ES6](http://meetup.toast.com/posts/85)를 읽어보세요. npm을 통해 Bable 패키지를 설치해야 합니다. $ npm install babel-core babel-preset-env babel-loader --save-dev `babel-core`와 `babel-preset-env`, `babel-loader` 세가지 패키지를 설치했습니다. `babel-core`와 `babel-preset-env`은 Babel의 핵심부이며, 자바스크립트의 어떤 기능을 트랜스파일할지 정의해놓은 패키지입니다. `babel-loader`는 앞서 설치한 `css-loader`와 `style-loader` 같은 로더입니다. `webpack.config.js`에서 로더를 추가해봅시다. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 9000 }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] } }; 이제 webpack이 실행될 때마다 `babel-loader`를 거치게 됩니다. `index.js`를 수정해서 ES6의 기능이 제대로 동작하는지 보죠. `import`는 `require`를 대체하는 ES6 문법입니다. 또한 `let`은 `var`와 달리 블록스코프를 갖는 타입으로 역시 ES6에서 추가되었습니다. 그리고 [템플릿 문자열](http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/)도 사용할 수 있습니다. // index.js import $ from 'jquery'; import './index.css'; let name = 'Park' console.log(`My name is ${name}`); console.log($('#title').text()); 좋습니다! 이제 배포 코드를 만드는 일만 남았네요. 이 부분은 다음 글에서 설명하도록 하겠습니다.
json metadata{"tags":["font-end","javascript","web","kr"],"links":["https://steemit.com/font-end/@caesium133/21-1","https://babeljs.io/","http://coffeescript.org/","https://gist.github.com/marocchino/841e2ff62f59f420f9d9","http://meetup.toast.com/posts/85","http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfont-end
permlink21-2-webpack
title21세기 프론트엔드 (2) webpack 확장하기
Transaction InfoBlock #19851069/Trx 8ecbcb9a452132cf4ab084fefd142b19e360ccfa
View Raw JSON Data
{
  "block": 19851069,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "[21세기 프론트엔드 (1) 시작하기](https://steemit.com/font-end/@caesium133/21-1)에서 npm과 webpack을 설치하고 기본적인 개발 환경을 세팅하는 작업을 했습니다. 이번 글에서는 webpack을 더 확장해서 자바스크립트 뿐만 아니라 CSS도 함께 번들링할 수 있도록 만들고, 더불어 개발환경에서 ES6를 사용하기 위해 [Babel](https://babeljs.io/)을 설치해보겠습니다. 여기까지 \"무슨 말인지 하나도 모르겠다!\"하셔도 괜찮습니다.\n\n# CSS 번들링하기\n\n이전 글에서 `index.js`와 jQuery를 번들링해서 `index.html`에서는 `bundle.js`만을 불러들이도록 했는데요, 여기에 CSS 파일도 추가해보려 합니다.\n\n    /* index.css */\n    h1 {\n      color: #ff0000;\n    }\n\n옛날 방법대로라면 `index.html`의 `<head>`태그 안에 `<style rel=\"stylesheet\" href=\"index.css\" />`와 같은 코드를 넣었겠지만, 이건 깔끔하지 않습니다. webpack을 활용해봅시다. 먼저 두 개의 로더를 설치해주세요.\n\n    $ npm install css-loader style-loader --save-dev\n\n둘 다 개발 환경에서만 사용할 것이기 때문에 개발 의존성으로 설치했습니다. 로더는 각 모듈을 특정 규칙에 따라 가공해줍니다. 로더를 설치했으니 `webpack.config.js`를 수정해주세요.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [ \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      }\n    };\n\n이제 정말 간단하게 CSS 파일을 번들링할 수 있습니다. 로더에 `style-loader`와 `css-loader`를 추가함으로써 webpack이 외부 CSS 파일을 읽고, 이것을 HTML 파일의 `<head>`태그에 `<style>`태그를 넣는 방식으로 CSS를 번들링할 수 있게 되었습니다. `index.js` 파일을 수정해보세요.\n\n    // index.js\n    var $ = require('jquery');\n    var style = require('./index.css');\n    console.log($('h1').text());\n\n이제 `npm run build`를 통해 webpack을 실행하면 `index.html`에 CSS가 적용된 것을 볼 수 있습니다. (`npm run watch`를 해두셨다면 `index.js`를 저장하자마자 자동으로 webpack이 실행됩니다.)\n\n# Babel 사용하기\n\nBabel은 트랜스파일러(Transpiler)입니다. 트랜스파일러는 ES6로 작성된 자바스크립트 코드를 브라우저와 호환되는 버전으로 바꿔줍니다. (브라우저가 자바스크립트 버전을 바로 따라가지 못하는 경우가 있기 때문에 트랜스파일러가 필요합니다.) 즉, 개발 환경에서 ES6의 기능들을 사용해도 괜찮다는 뜻입니다. 과거에는 [CoffeeScript](http://coffeescript.org/)가 주로 사용됐습니다. 만약 ES6가 무엇인지 잘 모르시는 분들은 [ES6시대의 JavaScript](https://gist.github.com/marocchino/841e2ff62f59f420f9d9) 또는 [지금 바로 시작하는 ES6](http://meetup.toast.com/posts/85)를 읽어보세요.\n\nnpm을 통해 Bable 패키지를 설치해야 합니다.\n\n    $ npm install babel-core babel-preset-env babel-loader --save-dev\n\n`babel-core`와 `babel-preset-env`, `babel-loader` 세가지 패키지를 설치했습니다. `babel-core`와 `babel-preset-env`은 Babel의 핵심부이며, 자바스크립트의 어떤 기능을 트랜스파일할지 정의해놓은 패키지입니다. `babel-loader`는 앞서 설치한 `css-loader`와 `style-loader` 같은 로더입니다. `webpack.config.js`에서 로더를 추가해봅시다.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 9000\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n            use: {\n              loader: 'babel-loader',\n              options: {\n                presets: ['env']\n              }\n            }\n          }, \n          {\n            test: /\\.css$/,\n            loader: 'style-loader!css-loader'\n          }\n        ]\n      }\n    };\n\n이제 webpack이 실행될 때마다 `babel-loader`를 거치게 됩니다. `index.js`를 수정해서 ES6의 기능이 제대로 동작하는지 보죠. `import`는 `require`를 대체하는 ES6 문법입니다. 또한 `let`은 `var`와 달리 블록스코프를 갖는 타입으로 역시 ES6에서 추가되었습니다. 그리고 [템플릿 문자열](http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/)도 사용할 수 있습니다.\n\n    // index.js\n    import $ from 'jquery';\n    import './index.css';\n\n    let name = 'Park'\n\n    console.log(`My name is ${name}`);\n    console.log($('#title').text());\n\n좋습니다! 이제 배포 코드를 만드는 일만 남았네요. 이 부분은 다음 글에서 설명하도록 하겠습니다.",
      "json_metadata": "{\"tags\":[\"font-end\",\"javascript\",\"web\",\"kr\"],\"links\":[\"https://steemit.com/font-end/@caesium133/21-1\",\"https://babeljs.io/\",\"http://coffeescript.org/\",\"https://gist.github.com/marocchino/841e2ff62f59f420f9d9\",\"http://meetup.toast.com/posts/85\",\"http://hacks.mozilla.or.kr/2015/08/es6-in-depth-template-strings-2/\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "font-end",
      "permlink": "21-2-webpack",
      "title": "21세기 프론트엔드 (2) webpack 확장하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-14T03:05:18",
  "trx_id": "8ecbcb9a452132cf4ab084fefd142b19e360ccfa",
  "trx_in_block": 10,
  "virtual_op": 0
}
caesium133updated their account properties
2018/02/13 04:01:00
accountcaesium133
json metadata{"profile":{"location":"Republic of Korea","website":"https://parksb.github.io/","profile_image":"https://i.imgur.com/PyMw8Cb.png","name":"Cs133","about":"Developer & Designer"}}
memo keySTM6vCF37qihYRr2AAjPRvgzctQUz4MfKkWA8s91KWTprv6XCSKVT
Transaction InfoBlock #19823410/Trx d8e7d4272dc6fe05bbf7ab70d81d8991b3915fc4
View Raw JSON Data
{
  "block": 19823410,
  "op": [
    "account_update",
    {
      "account": "caesium133",
      "json_metadata": "{\"profile\":{\"location\":\"Republic of Korea\",\"website\":\"https://parksb.github.io/\",\"profile_image\":\"https://i.imgur.com/PyMw8Cb.png\",\"name\":\"Cs133\",\"about\":\"Developer & Designer\"}}",
      "memo_key": "STM6vCF37qihYRr2AAjPRvgzctQUz4MfKkWA8s91KWTprv6XCSKVT"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T04:01:00",
  "trx_id": "d8e7d4272dc6fe05bbf7ab70d81d8991b3915fc4",
  "trx_in_block": 24,
  "virtual_op": 0
}
morningupvoted (85.00%) @caesium133 / 21-1
2018/02/13 03:39:18
authorcaesium133
permlink21-1
votermorning
weight8500 (85.00%)
Transaction InfoBlock #19822976/Trx 3ff1c8694ca3268beaa1633658e6e99064110a17
View Raw JSON Data
{
  "block": 19822976,
  "op": [
    "vote",
    {
      "author": "caesium133",
      "permlink": "21-1",
      "voter": "morning",
      "weight": 8500
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:39:18",
  "trx_id": "3ff1c8694ca3268beaa1633658e6e99064110a17",
  "trx_in_block": 13,
  "virtual_op": 0
}
filipinoupvoted (30.00%) @caesium133 / 21-1
2018/02/13 03:33:24
authorcaesium133
permlink21-1
voterfilipino
weight3000 (30.00%)
Transaction InfoBlock #19822858/Trx 5f7424eec30f989b38ed287c1056a699ad675bb5
View Raw JSON Data
{
  "block": 19822858,
  "op": [
    "vote",
    {
      "author": "caesium133",
      "permlink": "21-1",
      "voter": "filipino",
      "weight": 3000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:33:24",
  "trx_id": "5f7424eec30f989b38ed287c1056a699ad675bb5",
  "trx_in_block": 5,
  "virtual_op": 0
}
2018/02/13 03:27:57
authorcatfacts
body<p>Cats see six times better in the dark and at night than humans.</p>
json metadata{"app":"freakazoid/0.0.3","tags":["cat"]}
parent authorcaesium133
parent permlink63ddrw
permlinkre-caesium133--20180213t032714121z
title
Transaction InfoBlock #19822749/Trx b794d349496b3c3ea5093e8e79b4c911c66acdce
View Raw JSON Data
{
  "block": 19822749,
  "op": [
    "comment",
    {
      "author": "catfacts",
      "body": "<p>Cats see six times better in the dark and at night than humans.</p>\n",
      "json_metadata": "{\"app\":\"freakazoid/0.0.3\",\"tags\":[\"cat\"]}",
      "parent_author": "caesium133",
      "parent_permlink": "63ddrw",
      "permlink": "re-caesium133--20180213t032714121z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:27:57",
  "trx_id": "b794d349496b3c3ea5093e8e79b4c911c66acdce",
  "trx_in_block": 13,
  "virtual_op": 0
}
caesium133published a new post: 21-1
2018/02/13 03:26:57
authorcaesium133
body![Modern software development](https://i.imgur.com/KzCHMAx.gif) 솔직히 프론트엔드 개발에 대해 이 움짤보다 더 잘 설명할 자신이 없습니다. 당장 [2018년 웹 개발자 로드맵](https://github.com/kamranahmedse/developer-roadmap)만 봐도 배울게 넘칩니다. (그리고 지금 이 시간에도 계속해서 늘어나고 있습니다.) 이제 막 프론트엔드 개발을 시작하려는 분이나 오래전에 웹 개발을 배운 뒤 정보를 업데이트하지 않은 분들은 아득한 기분이 들겁니다. 그래도 막상 시작해보면 생각만큼 어렵지는 않습니다. 모든 것이 서로 얽혀있기 때문에 하나를 배우면 동시에 다른 하나를 같이 배우게 됩니다. 이번 글에서만 npm과 npm scripts, webpack을 다룹니다. 너무 걱정하지 마세요. # 오래된 방법 처음 웹 프로그래밍을 배울 때는 HTML 파일에 외부 CSS나 자바스크립트 코드를 `<link>`태그나 `<script>`태그로 가져오라고 배웁니다. 이렇게요: <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="index.js"></script> </body> </html> 이정도만 해도 웹사이트는 잘 돌아갑니다. 하지만 많은 점이 불편합니다. * 만약 자바스크립트 코드가 방대해져서 파일을 30개로 쪼갠다면 `index.html`에는 30개의 script태그가 필요하게 됩니다. CSS도 마찬가지구요. * 뿐만 아니라 외부 자바스크립트 라이브러리를 가져올 때도 문제가 생깁니다. 만일 jQuery 3.3.1 버전을 다운받아서 `<script src="jquery-3.3.1.min.js"></script>`와 같은 코드를 작성한다면 jQuery 3.4.0 버전이 나왔을 땐 다시 코드를 다운받는 수고를 해야합니다. * 다른 사람과 프로젝트를 공유할 때도 불편합니다. 어떤 파일이 어떤 라이브러리나 프레임워크를 사용하고 있는지 일일히 설명해야하고, 또 그 파일들을 모두 공유해야 하니까요. # npm(node package manager) 사용하기 그래서 npm이라고 하는 '패키지 매니저'를 사용합니다. (원래는 대부분 [Bower](https://bower.io/)를 사용했는데, 이젠 npm이 더 인기있습니다.) npm은 마치 앱스토어에서 어플리케이션을 다운받아 쓰듯이 자바스크립트 패키지를 쉽게 찾고 다운받아서 웹사이트에 적용시킬 수 있도록 만들어줍니다. 이렇게 하면 해당 웹사이트가 어떤 패키지를 사용하고 있는지 한 눈에 볼 수 있고, 패키지의 버전 관리도 훨씬 쉬워집니다. npm은 Node.js를 기반으로 합니다. [Node.js 홈페이지](https://nodejs.org/ko/)에서 안정적인 LTS버전을 다운받으세요. Node.js를 설치하면 npm도 설치됩니다. npm을 사용한다는 것은 CLI 환경에서 작업을 한다는 것을 의미합니다. 많은 분들이 커맨드 라인을 보면 겁을 먹는데, 너무 걱정하지 않으셔도 됩니다. 익숙해지면 오히려 GUI보다 편합니다. 자, 설치가 끝났다면 이제 터미널이나 cmd를 열고 잘 설치되었는지 확인해보세요. Node.js 버전 확인: $ node -v npm 버전 확인: $ npm -v 이제 21세기 프론트엔드 개발 환경 세팅에 첫 삽을 떴습니다. `index.html`이 있는 작업 디렉토리로 이동해서 npm 환경을 초기화해줍니다. $ npm init 이것저것 설정하는 질문이 뜰텐데 그냥 모두 엔터를 눌러주세요. 나중에 수정할 수 있습니다. 설정이 끝났다면 `index.html`이 있는 디렉토리에 `package.json`이라는 새로운 파일이 생깁니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } 이제 npm으로 jQuery를 설치해볼까요? $ npm install jquery 이 명령어를 실행하면 npm의 중앙 저장소에서 jQuery를 다운받아 `node_modules`라는 폴더에 저장합니다. `package.json`을 새로고침하면 내용이 바뀐 것을 볼 수 있습니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "jquery": "^3.3.1" } } 정말 편합니다. 이제 다른 사람과 프로젝트를 함께 할 때 `node_modules` 폴더를 주지 않고 `package.json` 파일을 주면 상대방은 `$ npm install` 명령어를 통해 필요한 자바스크립트 패키지를 한번에 다운받을 수 있게 됩니다. `node_modules/jquery/dist/`폴더를 보면 jQuery 코드들이 모여있는 것을 볼 수 있습니다. <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="node_modules/jquery/dist/jquery.min.js"></script> <script src="index.js"></script> </body> </html> 이렇게 하면 `index.js`에서 jQuery를 사용할 수 있습니다. 하지만 패키지를 설치하고 불러 올때마다 `node_moduels` 폴더를 뒤져봐야 하는 것은 여전히 불편합니다. 하지만 이것도 쉽게 해결할 수 있습니다. // index.js var $ = require('jquery'); console.log($('#title').text()); `jquery`는 전역에서 사용 가능하며, Node.js는 서버사이드 언어이기 때문에 패키지의 경로를 따로 지정해주지 않아도 모두 알아서 패키지를 가져와 줍니다. `index.html`에서 jQuery를 불러오는 `<script>`태그를 지우고 사이트를 새로고침해보세요. 어떤가요? 아쉽지만 브라우저는 `require`를 모르기 때문에 에러가 뜹니다. (브라우저는 파일 시스템에 접근할 권한을 가지고 있지 않습니다.) 그래서 우리는 webpack를 사용합니다. # webpack 사용하기 webpack은 '모듈 번들러'입니다. [Browserify](http://browserify.org/)라는 모듈 번들러도 있는데, webpack은 번들링 뿐만 아니라 부가적인 기능을 많이 제공하고 있어 매우 편리합니다. 번들링이라는 것은 여러 자바스크립트 파일들을 하나로 합친다는 의미입니다. 현재 `index.html`에서는 `jquery.min.js`와 `index.js`를 따로 불러들이고 있는데, webpack을 사용하면 `bundle.js`와 같은 파일 하나만 부를 수 있게 됩니다. 또한 앞서 사용하지 못한 `require`도 사용할 수 있게 됩니다. 그럼 webpack을 설치해봅니다. $ npm install webpack --save-dev `--save-dev`은 개발 의존으로 설치하겠다는 의미입니다. 즉, webpack이 개발용으로만 쓰이고 실제 배포판에서는 사용되지 않을 것이라는 뜻입니다. `package.json`을 확인해보면 아까 설치한 jquery와는 다르게 `devDependencies`로 설치된 것을 볼 수 있습니다. 그럼 이제 webpack을 세팅해볼까요? webpack을 세팅하기 위해서는 `webpack.config.js`라는 파일을 만들어야 합니다. 파일 이름은 꼭 `webpack.config.js`이어야 webpack이 실행될 때 이 파일을 참조하게 됩니다. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' } }; `entry`는 번들링할 파일, `output`은 번들링한 결과물로 나올 파일을 의미합니다. 우리는 `index.js`를 번들링해서 `bundle.js`로 내보낼 것입니다. `index.html`을 수정합시다. <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1>Hello, world!</h1> <script src="bundle.js"></script> </body> </html> 그리고 webpack을 실행하면! $ ./node_modules/.bin/webpack `bundle.js`가 만들어지고, 사이트도 잘 돌아갑니다. 하지만 매번 `index.js`를 수정할 때마다 webpack을 실행해주는 건 불편합니다. 이건 어떡할까요? # task runner 사용하기 task runner는 빌드를 자동화해주는 도구입니다. 예전에는 [Grunt](https://gruntjs.com/)나 [Gulp](https://gulpjs.com/)가 쓰였지만, 요즘은 npm에 포함되어 있는 스크립트 기능을 사용하는 추세입니다. 별도의 패키지를 설치할 필요 없이 `package.json`만 수정하면 바로 되니까 정말 편합니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", }, "dependencies": { "jquery": "^3.3.1" } } `build`와 `watch`라는 명령어를 추가했습니다. $ npm run build `build`를 하면 webpack이 실행되며 코드를 빌드해줍니다. UglifyJS와 같은 플러그인을 설치하고 스크립트를 조금 수정하면 minify된 배포용 코드를 만들 수도 있습니다. 이건 다음 글에서 다루겠습니다. $ npm run watch `watch`는 자바스크립트 파일이 수정될 때마다 자동으로 webpack을 실행시켜줍니다. 한 번만 켜두면 자바스크립트 코드를 수정하고 저장할 때마다 webpack이 실행되어 모든 코드가 번들링됩니다. 이제 여기에 웹 서버를 돌리면 개발환경을 더 편리하게 만들 수 있습니다. # 웹 서버 사용하기 아파치나 Nginx를 설치하지 않아도 웹서버를 구축할 수 있습니다. webpack이 제공하는 웹서버를 설치하면 됩니다. $ npm install webpack-dev-server --save-dev 설치가 끝나면 `package.json`에 스크립트를 추가해줍니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", "webpack-dev-server": "^2.11.1" }, "dependencies": { "jquery": "^3.3.1" } } `build`와 `watch`처럼 `server`를 사용할 수 있게 되었습니다. 그리고 `webpack.config.js`에도 서버에 대한 설정을 추가해줍니다. module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 8000 }, }; 그리고 서버를 실행하세요. $ npm run server 이렇게 하면 `webpack.config.js`가 있는 디렉토리의 `index.html`이 `localhost:8000`에서 열립니다. 자바스크립트 파일을 수정하면 브라우저를 새로고침해주기도 합니다. 정말 엄청나게 편해졌죠. 이제 기본적인 세팅을 모두 끝냈습니다. 이번 글에서 우리는 5개의 파일(`index.html`, `index.js`, `bundle.js`, `package.json`, `webpack.config.js`)과 하나의 폴더(`node_modules`)를 얻었습니다. **index.html** <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="bundle.js"></script> </body> </html> **index.js** var $ = require('jquery'); console.log($('#title').text()); **package.json** { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", "webpack-dev-server": "^2.11.1" }, "dependencies": { "jquery": "^3.3.1" } } **webpack.config.js** module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 8000 }, };
json metadata{"tags":["font-end","javascript","web","kr"],"image":["https://i.imgur.com/KzCHMAx.gif"],"links":["https://github.com/kamranahmedse/developer-roadmap","https://bower.io/","https://nodejs.org/ko/","http://browserify.org/","https://gruntjs.com/","https://gulpjs.com/"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfont-end
permlink21-1
title21세기 프론트엔드 (1) 시작하기
Transaction InfoBlock #19822729/Trx f89775f0c683be8d440d2d4bf232a4a6d562c861
View Raw JSON Data
{
  "block": 19822729,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "![Modern software development](https://i.imgur.com/KzCHMAx.gif)\n\n솔직히 프론트엔드 개발에 대해 이 움짤보다 더 잘 설명할 자신이 없습니다. 당장 [2018년 웹 개발자 로드맵](https://github.com/kamranahmedse/developer-roadmap)만 봐도 배울게 넘칩니다. (그리고 지금 이 시간에도 계속해서 늘어나고 있습니다.) 이제 막 프론트엔드 개발을 시작하려는 분이나 오래전에 웹 개발을 배운 뒤 정보를 업데이트하지 않은 분들은 아득한 기분이 들겁니다. 그래도 막상 시작해보면 생각만큼 어렵지는 않습니다. 모든 것이 서로 얽혀있기 때문에 하나를 배우면 동시에 다른 하나를 같이 배우게 됩니다. 이번 글에서만 npm과 npm scripts, webpack을 다룹니다. 너무 걱정하지 마세요.\n\n# 오래된 방법\n\n처음 웹 프로그래밍을 배울 때는 HTML 파일에 외부 CSS나 자바스크립트 코드를 `<link>`태그나 `<script>`태그로 가져오라고 배웁니다. 이렇게요:\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"index.js\"></script>\n    </body>\n    </html>\n\n이정도만 해도 웹사이트는 잘 돌아갑니다. 하지만 많은 점이 불편합니다.\n\n*   만약 자바스크립트 코드가 방대해져서 파일을 30개로 쪼갠다면 `index.html`에는 30개의 script태그가 필요하게 됩니다. CSS도 마찬가지구요.\n*   뿐만 아니라 외부 자바스크립트 라이브러리를 가져올 때도 문제가 생깁니다. 만일 jQuery 3.3.1 버전을 다운받아서 `<script src=\"jquery-3.3.1.min.js\"></script>`와 같은 코드를 작성한다면 jQuery 3.4.0 버전이 나왔을 땐 다시 코드를 다운받는 수고를 해야합니다.\n*   다른 사람과 프로젝트를 공유할 때도 불편합니다. 어떤 파일이 어떤 라이브러리나 프레임워크를 사용하고 있는지 일일히 설명해야하고, 또 그 파일들을 모두 공유해야 하니까요.\n\n# npm(node package manager) 사용하기\n\n그래서 npm이라고 하는 '패키지 매니저'를 사용합니다. (원래는 대부분 [Bower](https://bower.io/)를 사용했는데, 이젠 npm이 더 인기있습니다.) npm은 마치 앱스토어에서 어플리케이션을 다운받아 쓰듯이 자바스크립트 패키지를 쉽게 찾고 다운받아서 웹사이트에 적용시킬 수 있도록 만들어줍니다. 이렇게 하면 해당 웹사이트가 어떤 패키지를 사용하고 있는지 한 눈에 볼 수 있고, 패키지의 버전 관리도 훨씬 쉬워집니다.\n\nnpm은 Node.js를 기반으로 합니다. [Node.js 홈페이지](https://nodejs.org/ko/)에서 안정적인 LTS버전을 다운받으세요. Node.js를 설치하면 npm도 설치됩니다. npm을 사용한다는 것은 CLI 환경에서 작업을 한다는 것을 의미합니다. 많은 분들이 커맨드 라인을 보면 겁을 먹는데, 너무 걱정하지 않으셔도 됩니다. 익숙해지면 오히려 GUI보다 편합니다. 자, 설치가 끝났다면 이제 터미널이나 cmd를 열고 잘 설치되었는지 확인해보세요.\n\nNode.js 버전 확인:\n\n    $ node -v\n\nnpm 버전 확인:\n\n    $ npm -v\n\n이제 21세기 프론트엔드 개발 환경 세팅에 첫 삽을 떴습니다. `index.html`이 있는 작업 디렉토리로 이동해서 npm 환경을 초기화해줍니다.\n\n    $ npm init\n\n이것저것 설정하는 질문이 뜰텐데 그냥 모두 엔터를 눌러주세요. 나중에 수정할 수 있습니다. 설정이 끝났다면 `index.html`이 있는 디렉토리에 `package.json`이라는 새로운 파일이 생깁니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\"\n    }\n\n이제 npm으로 jQuery를 설치해볼까요?\n\n    $ npm install jquery\n\n이 명령어를 실행하면 npm의 중앙 저장소에서 jQuery를 다운받아 `node_modules`라는 폴더에 저장합니다. `package.json`을 새로고침하면 내용이 바뀐 것을 볼 수 있습니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n정말 편합니다. 이제 다른 사람과 프로젝트를 함께 할 때 `node_modules` 폴더를 주지 않고 `package.json` 파일을 주면 상대방은 `$ npm install` 명령어를 통해 필요한 자바스크립트 패키지를 한번에 다운받을 수 있게 됩니다. `node_modules/jquery/dist/`폴더를 보면 jQuery 코드들이 모여있는 것을 볼 수 있습니다.\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"node_modules/jquery/dist/jquery.min.js\"></script>\n      <script src=\"index.js\"></script>\n    </body>\n    </html>\n\n이렇게 하면 `index.js`에서 jQuery를 사용할 수 있습니다. 하지만 패키지를 설치하고 불러 올때마다 `node_moduels` 폴더를 뒤져봐야 하는 것은 여전히 불편합니다. 하지만 이것도 쉽게 해결할 수 있습니다.\n\n    // index.js\n    var $ = require('jquery');\n    console.log($('#title').text());\n\n`jquery`는 전역에서 사용 가능하며, Node.js는 서버사이드 언어이기 때문에 패키지의 경로를 따로 지정해주지 않아도 모두 알아서 패키지를 가져와 줍니다. `index.html`에서 jQuery를 불러오는 `<script>`태그를 지우고 사이트를 새로고침해보세요. 어떤가요? 아쉽지만 브라우저는 `require`를 모르기 때문에 에러가 뜹니다. (브라우저는 파일 시스템에 접근할 권한을 가지고 있지 않습니다.) 그래서 우리는 webpack를 사용합니다.\n\n# webpack 사용하기\n\nwebpack은 '모듈 번들러'입니다. [Browserify](http://browserify.org/)라는 모듈 번들러도 있는데, webpack은 번들링 뿐만 아니라 부가적인 기능을 많이 제공하고 있어 매우 편리합니다. 번들링이라는 것은 여러 자바스크립트 파일들을 하나로 합친다는 의미입니다. 현재 `index.html`에서는 `jquery.min.js`와 `index.js`를 따로 불러들이고 있는데, webpack을 사용하면 `bundle.js`와 같은 파일 하나만 부를 수 있게 됩니다. 또한 앞서 사용하지 못한 `require`도 사용할 수 있게 됩니다. 그럼 webpack을 설치해봅니다.\n\n    $ npm install webpack --save-dev\n\n`--save-dev`은 개발 의존으로 설치하겠다는 의미입니다. 즉, webpack이 개발용으로만 쓰이고 실제 배포판에서는 사용되지 않을 것이라는 뜻입니다. `package.json`을 확인해보면 아까 설치한 jquery와는 다르게 `devDependencies`로 설치된 것을 볼 수 있습니다.\n\n그럼 이제 webpack을 세팅해볼까요? webpack을 세팅하기 위해서는 `webpack.config.js`라는 파일을 만들어야 합니다. 파일 이름은 꼭 `webpack.config.js`이어야 webpack이 실행될 때 이 파일을 참조하게 됩니다.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      }\n    };\n\n`entry`는 번들링할 파일, `output`은 번들링한 결과물로 나올 파일을 의미합니다. 우리는 `index.js`를 번들링해서 `bundle.js`로 내보낼 것입니다. `index.html`을 수정합시다.\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1>Hello, world!</h1>\n      <script src=\"bundle.js\"></script>\n    </body>\n    </html>\n\n그리고 webpack을 실행하면!\n\n    $ ./node_modules/.bin/webpack\n\n`bundle.js`가 만들어지고, 사이트도 잘 돌아갑니다. 하지만 매번 `index.js`를 수정할 때마다 webpack을 실행해주는 건 불편합니다. 이건 어떡할까요?\n\n# task runner 사용하기\n\ntask runner는 빌드를 자동화해주는 도구입니다. 예전에는 [Grunt](https://gruntjs.com/)나 [Gulp](https://gulpjs.com/)가 쓰였지만, 요즘은 npm에 포함되어 있는 스크립트 기능을 사용하는 추세입니다. 별도의 패키지를 설치할 필요 없이 `package.json`만 수정하면 바로 되니까 정말 편합니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n`build`와 `watch`라는 명령어를 추가했습니다.\n\n    $ npm run build\n\n`build`를 하면 webpack이 실행되며 코드를 빌드해줍니다. UglifyJS와 같은 플러그인을 설치하고 스크립트를 조금 수정하면 minify된 배포용 코드를 만들 수도 있습니다. 이건 다음 글에서 다루겠습니다.\n\n    $ npm run watch\n\n`watch`는 자바스크립트 파일이 수정될 때마다 자동으로 webpack을 실행시켜줍니다. 한 번만 켜두면 자바스크립트 코드를 수정하고 저장할 때마다 webpack이 실행되어 모든 코드가 번들링됩니다. 이제 여기에 웹 서버를 돌리면 개발환경을 더 편리하게 만들 수 있습니다.\n\n# 웹 서버 사용하기\n\n아파치나 Nginx를 설치하지 않아도 웹서버를 구축할 수 있습니다. webpack이 제공하는 웹서버를 설치하면 됩니다.\n\n    $ npm install webpack-dev-server --save-dev\n\n설치가 끝나면 `package.json`에 스크립트를 추가해줍니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\",\n        \"server\": \"webpack-dev-server --open\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n        \"webpack-dev-server\": \"^2.11.1\"\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n`build`와 `watch`처럼 `server`를 사용할 수 있게 되었습니다. 그리고 `webpack.config.js`에도 서버에 대한 설정을 추가해줍니다.\n\n        module.exports = {\n          entry: './index.js',\n          output: {\n            filename: 'bundle.js'\n          },\n          devServer: {\n            contentBase: './',\n            compress: true,\n            port: 8000\n          },\n        };\n\n그리고 서버를 실행하세요.\n\n    $ npm run server\n\n이렇게 하면 `webpack.config.js`가 있는 디렉토리의 `index.html`이 `localhost:8000`에서 열립니다. 자바스크립트 파일을 수정하면 브라우저를 새로고침해주기도 합니다. 정말 엄청나게 편해졌죠. 이제 기본적인 세팅을 모두 끝냈습니다. 이번 글에서 우리는 5개의 파일(`index.html`, `index.js`, `bundle.js`, `package.json`, `webpack.config.js`)과 하나의 폴더(`node_modules`)를 얻었습니다.\n\n**index.html**\n\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"bundle.js\"></script>\n    </body>\n    </html>\n\n**index.js**\n\n    var $ = require('jquery');\n    console.log($('#title').text());\n\n**package.json**\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\",\n        \"server\": \"webpack-dev-server --open\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n        \"webpack-dev-server\": \"^2.11.1\"\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n**webpack.config.js**\n\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 8000\n      },\n    };",
      "json_metadata": "{\"tags\":[\"font-end\",\"javascript\",\"web\",\"kr\"],\"image\":[\"https://i.imgur.com/KzCHMAx.gif\"],\"links\":[\"https://github.com/kamranahmedse/developer-roadmap\",\"https://bower.io/\",\"https://nodejs.org/ko/\",\"http://browserify.org/\",\"https://gruntjs.com/\",\"https://gulpjs.com/\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "font-end",
      "permlink": "21-1",
      "title": "21세기 프론트엔드 (1) 시작하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:26:57",
  "trx_id": "f89775f0c683be8d440d2d4bf232a4a6d562c861",
  "trx_in_block": 0,
  "virtual_op": 0
}
caesium133published a new post: 4dkun
2018/02/13 03:26:30
authorcaesium133
body프로젝트하면서 알게 된 것들과 코딩테스트를 통해 배운 것들, 자바스크립트&제이쿼리: 인터랙티브 프론트엔드 웹 개발 교과서(Jon Duckett)와 JavaScript: The Good Parts(Douglas Crockford)를 통해 공부한 것들, 그리고 [Front-end Job Interview Questions](https://github.com/h5bp/Front-end-Developer-Interview-Questions)에 나와있는 질문들에 대한 대답을 간략하게 정리했습니다. 정보의 출처는 글마다 참고 링크를 걸어두었습니다. 잘못된 내용이 있다면 댓글로 달아주세요! # 일반적인 것들 ## 헝가리안 표기법 변수 이름에 데이터 타입을 접두어로 붙이는 표기법입니다. 숫자라면 nVar, 배열이라면 aVar 이런 식이으로 붙입니다. 2015년 네이버에서 인턴십을 할 때 코딩컨벤션이 헝가리언 표기법 + 캐멀케이스여서 그때 처음 접하고 사용하기 시작했는데, 요즘에는 헝가리언 표기법을 지양한다고 합니다. 가독성과 유지보수 효율성이 떨어진다네요. ([참고](https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation)) ## 블록 안에서 변수 선언 Java의 경우 한 블록 안에서만 쓰이는 변수를 코드 위쪽에 선언하지 않고 블록 안에 변수를 선언하면 코드를 파악하기 더욱 쉬워지고, 스코프를 제한할 수 있어 안전합니다. 자바스크립트의 `var`타입은 블록 스코프가 아닌 함수 스코프를 적용하기 때문에 어디에 선언하든 아무런 차이가 없습니다. 하지만 `let`이나 `const`를 사용한다면 얘기가 달라집니다. 이 둘은 블록 스코프입니다. ([참고](https://stackoverflow.com/questions/3684923/javascript-variables-declare-outside-or-inside-loop)) ## 점진적 향상법(Progressive enhancement)과 우아한 성능저하법(Graceful degradation) 사용자가 항상 최신 기술을 사용할 수 있는 환경에서 서비스를 사용하지는 않습니다. 특히 웹사이트를 만들 때는 최신 브라우저와 구식 브라우저를 모두 신경써야 합니다. 점진적 향상법과 우아한 성능저하법은 최신과 구식에 대응하기 위한 방법을 말합니다. ([참고](http://www.clearboth.org/51_graceful_degradation_versus_progressive_enhancement/)) 점진적 향상법은 기본적으로 구식 기술 환경에서 동작할 수 있는 기능을 구현하고, 최신 기술을 사용할 수 있는 환경에서는 더 나은 사용자 경험을 제공할 수 있는 최신 기술을 제공하는 방법입니다. 즉, 구식 환경에서도 충분히 서비스를 사용할 수 있고, 최신 환경이라면 더 나은 기능들을 사용할 수 있도록 만드는 것입니다. 구식 브라우저를 사용하는 사용자에게 100만큼의 기능을 제공하고, 최신 브라우저를 사용하는 사용자에게는 130정도의 기능을 제공하도록 웹사이트를 만든다고 보면 됩니다. 우아한 성능저하법은 점진적 향상법과 반대입니다. 이는 최신 기술을 기반으로 기능을 구현한 뒤, 구형 기술에 기반한 환경에서도 유사하게 동작하도록 만드는 방법입니다. 최신 브라우저에서는 100만큼의 기능을 제공하고, 구식 브라우저에서는 50정도의 기능만을 제공하게 웹사이트를 만드는 것입니다. img 태그에 alt 속성을 지정함으로써 이미지를 보여주지 못하는 환경에서 이미지를 텍스트로 대체하는 것이 대표적인 예시입니다. ## FOUC(Flash Of Unstyled Content) FOUC는 외부의 CSS 코드를 불러오기 전 스타일이 적용되지 않은 페이지가 잠시 나타나는 현상입니다. 특히 IE에서 자주 발생하는 현상으로, 사옹자 경험을 저하하는 요인이 됩니다. FOUC가 발생하는 주요 원인은 브라우저가 마크업에 참조된 파일들을 모아 DOM을 생성할 때 가장 빠르게 분석할 수 있는 부분(HTML)을 먼저 화면에 표시한 뒤 화면에 출력된 마크업 순서에 따라 스타일(CSS)을 적용하고 스크립트(Javascript)를 실행하기 때문입니다. FOUC를 해결하기 위해서는 head 태그 안에 CSS를 링크하고, @import의 사용을 자제해야 합니다. 또한 자바스크립트를 head 태그 안에서 로드하는 것도 방법될 수 있습니다. (하지만 별로 추천하지 않습니다.) 어떤 방법으로도 해결되지 않는다면 FOUC가 발생하는 구역을 숨겼다가 브라우저가 준비됐을 때 다시 보여주는 방법이 있습니다. ([참고](http://webdir.tistory.com/416)) ## ARIA(Accessible Rich Internet Applications)와 스크린리더 ARIA는 접근가능한 인터넷 어플리케이션을 의미합니다. 이는 웹 콘텐츠를 개발할 때 장애인을 위한 접근성 향상 방법을 정의합니다. ARIA를 사용해 웹사이트여러 곳의 접근성을 향상할 수 있습니다. ARIA는 html 태그에 role 속성을 지정하는 방식으로 사용할 수 있으며, 대부분의 브라우저와 스크린리더가 ARIA를 지원하고 있습니다. 스크린리더는 웹사이트의 구성 요소들을 읽어주는 프로그램으로, 시각장애를 가진 사용자가 컴퓨터 화면에 무엇이 있는지 인지할 수 있게 돕습니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/Accessibility/ARIA)) ([참고2](http://www.bloter.net/archives/148889)) ## CSS 애니메이션과 Javascript 애니메이션의 차이 자바스크립트는 메인 쓰레드가 무거운 작업을 하고 있을 때 애니메이션 처리의 우선순위를 미뤄두는 반면 CSS는 독립적인 쓰레드가 애니메이션을 처리해줍니다. 때문에 CSS 애니메이션은 최적화가 쉽습니다. 하지만 항상 CSS 애니메이션이 우수한 것은 아닙니다. UI 요소가 작은 경우 CSS를 사용하고, 애니메이션을 세밀하게 제어해야 하는 경우 자바스크립트를 사용하는 것이 좋습니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy)) ([참고2](https://brunch.co.kr/@99-life/2)) ## 동일출처정책(Same-origin policy)과 CORS(Cross-origin resource sharing) 동일출처정책은 한 출처의 문서가 다른 출처의 문서와 상호작용하지 못하도록하는 정책입니다. 두 문서의 프로토콜, 포트, 호스트가 같으면 동일출처라고 합니다. CORS는 어떤 웹페이지에 있는 자바스크립트가 해당 도메인이 아닌 다른 도메인에 XMLHttpRequests 요청을 허용하는 기법을 말합니다. 브라우저는 서버와 통신할 때마다 서로에 대한 정보를 HTTP 헤더를 이용해 전달합니다. CORS는 HTTP 헤더에 정보를 추가해 브라우저와 서버가 서로 통신해야 한다는 사실을 알게 합니다. CORS를 통하지 않을 경우, Cross-domain 요청은 동일출처정책에 의해 브라우저가 금지합니다. CORS는 다른 출처에서 온 요청을 허용할 지 결정하기 위해 브라우저와 서버가 상호교류하는 방법을 정의한 것입니다. CORS는 W3C 명세에 포함되어 있지만 IE의 경우에는 비표준 XDomainRequest 객체를 사용하여 CORS 요청을 처리합니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy)) ([참고2](http://www.internetmap.kr/entry/Crossorigin-resource-sharing)) ## 코드 의존성과 DRY원칙 어떤 스크립트는 다른 추가적인 스크립트를 필요로 하기도 합니다. 이때 다른 스크립트에 의존하는 스크립트를 작성할 때 해당 스크립트에 의존성이 있다고 표현합니다. jQuery를 활용하는 스크립트는 의존성이 있다고 할 수 있습니다. 이런 경우 주석을 통해 의존성을 명시하는 것이 좋습니다. DRY원칙은 Don't Repeat Yourself의 줄임말로, 소프트웨어 개발 원칙을 말합니다. 한국어로는 중복배제라고 합니다. 같은 작업을 수행하는 코드를 두 번 작성했다는 이를 코드 중복이라고 하는데, DRY원칙은 이러한 코드 중복을 지양하자는 원칙입니다. 코드 중복의 반대 개념으로는 코드 재사용(Code reuse)이 있습니다. 코드 재사용은 같은 코드가 스크립트의 다른 곳에서 한 번 이상 사용되는 것을 말합니다. 함수를 활용하는 것은 코드 재사용의 좋은 예로, 재사용 가능한 함수를 헬퍼 함수(Helper function)라고 합니다. 코드 재사용을 권장하기 위해 개발자들은 작은 스크립트를 작성하는데, 이때문에 코드 재사용은 코드 사이에 더 많은 의존성을 만듭니다. # HTML ## DOCTYPE DOCTYPE은 문서형을 말하며, 해당 문서가 어떤 버전의 어떤 마크업 언어로 구성되어있는지를 의미합니다. DOCTYPE을 선언하는 것을 DTD(Dodumenttation Type Declaration)라고 한다. DTD는 각 마크업의 각 버전에서 사용가능한 태그나 속성 등을 정의하기 때문에 문서의 최상단에 위치해야 합니다. HTML5 이전 버전은 SGML(Standard Generalized Markup Language)에 기반했기 때문에 DTD 참조가 필수적입니다. HTML5는 DTD 참조가 필요하지 않으며, 하위 호환을 위해 `<!DOCTYPE html>`만으로 선언합니다. ([참고](http://webdir.tistory.com/40)) ## 표준모드(Standard mode)와 호환모드(Quirks mode) 표준모드와 호환모드는 브라우저가 가진 두 가지 렌더링 모드입니다. 브라우저는 DTD에 따라 렌더링할 모드를 선택하는데, 이 과정을 Doctype sniffing또는 Doctype switching이라고 합니다. 브라우저가 출력하고자 하는 문서가 최신이라면 W3C나 IETF의 표준을 엄격히 준수하는 표준모드로 렌더링을 합니다. 반면 문서가 오래된 버전이라면 호환모드로 렌더링합니다. 호환모드는 이전 세대의 브라우저에 맞는 비표준 규칙을 문서에 적용해 오래된 웹페이지들이 최신 브라우저에서 깨져보이지 않게 합니다. 브라우저는 문서가 최신인지 아닌지를 DTD로 판단한다. 만약 DTD가 존재하지 않거나 일부가 누락된 경우 호환모드로 문서를 해석합니다. 또한 IE의 경우 DTD 앞에 주석이나 다른 문자가 들어갔을 때도 문서를 호환모드로 해석한다. ([참고](https://developer.mozilla.org/ko/docs/Web/HTML/Quirks_Mode_and_Standards_Mode)) ## XML과 XHTML XML과 XHTML모두 웹 문서 규격입니다. XML은 W3C에서 여러 특수 목적의 마크업 언어를 만드는 용도에서 권장되는 다목적 마크업 언어입니다. XML은 문서 상의 데이터 이름과 값 등을 구분하기 위해 만들어졌는데요, XML은 SGML(참고로 SGML은 인터넷이 등장하기 이전에 만들어졌다.)을 기반으로한 HTML의 한계를 극복하여 XHTML을 이끌었습니다. XML을 기반으로 한 HTML을 만든 셈입니다. XHTML은 XML의 문법을 따르며, HTML 문법과 매우 유사하지만 더 엄격합니다. XHTML이 더 표준인 것처럼 보이지만 사실 그렇게 권장하지 않는 사람들도 있습니다. 일단 HTML의 호환성이 더 높기 때문입니다. XHTML은 1.1버전부터 비표준이나 비권장 태그를 호환하지 못하게 되면서 지나치게 엄격하다는는 비판을 받았습니다. IE가 XHTML을 해석하지 못하는 것 역시 XHTML의 호환성을 떨어뜨리는 요인이 됐습니다. 또한 XHTML과 HTML의 요소와 속성에는 차이가 거의 없습니다. 단지 HTML에서는 `<br>`로 써도 되지만 XHTML에서는 반드시 `<br/>`로 써야한다는 정도의 문법이 다를 뿐입니다. 이런 점에서 굳이 XHTML을 써야할 기술적 이유는 없다는 것입니다. 한편 HTML5가 발표되면서 XHTML은 거의 사용되지 않고 있습니다. ([참고](http://blog.wystan.net/2007/05/24/xhtml-vs-html)) ## data-* 속성 HTML5에서 새로 추가된 data- _속성은 커스텀 데이터 속성으로, 개발자가 임의로 이름을 붙일 수 있는 속성입니다. data-_ 속성은 html 태그 상에서 별다른 작용을 하지 않습니다. 자바스크립트가 DOM의 데이터에 접근하거나 서버에서 받아온 데이터를 활용해야 할 때 사용됩니다. ([참고](https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/data-*)) ## Cookie, sessionStorage, localStorage 쿠키(Cookie), 세션 저장소(sessionStorage), 로컬 저장소(localStorage)는 브라우저에 데이터를 저장하기 위한 공간들입니다. HTML5 이전에는 쿠키를 주로 사용했습니다. 하지만 쿠키는 많은 양의 데이터를 저장할 수 없고, 동일한 도메인에 페이지를 요청할 때마다 서버로 함께 전송되며, 변조가 쉬워 보안이 취약합니다. 그래서 HTML5부터는 저장소 객체(Storage object)를 정의하고 있습니다. 저장소 객체는 세션 저장소와 로컬 저장소 두 가지를 제공합니다. 로컬 저장소에 데이터를 자장하면 창이나 탭을 닫아서 세션이 종료돼도 데이터가 보존되고, 열려 있는 모든 창이나 탭이 데이터를 공유하게 됩니다. 세션 저장소는 반대입니다. 일반적으로 브라우저는 저장소에 5mb 정도의 공간을 할당하며, 데이터는 키-값 쌍(KVP; Key-Value Pair)을 이용하는 저장소 객체의 속성으로 저장됩니다. 또한 브라우저는 데이터를 보호하기 위해 동일출처정책에 의거, 서로 다른 페이지는 같은 도메인에 저장된 데이터에만 접근이 가능하도록 제한하고 있습니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies)) ([참고2](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) ([참고3](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)) ## script 태그의 async와 defer script 태그에 async 속성과 defer 속성이 추가된 것은 HTML5부터입니다. 브라우저가 웹 문서에서 외부 스크립트를 불러오는 script 태그를 만나면 해당 스크립트를 내려받아 해석하고 실행할 때까지 HTML 코드 파싱 작업을 뒤로 미룹니다. 그래서 무거운 스크립트 문서를 해석할 때는 페이지 전체가 느려지는 현상이 발생합니다. 그런데 script 태그에 async나 defer 속성을 지정하면 마크업 코드 작업을 중단하지 않고 스크립트를 동시에 내려받게 됩니다. defer는 마크업 파싱을 마친 다음 스크립트를 실행하며, async는 스크립트를 내려받는 즉시 스크립트를 실행합니다. ([참고](https://appletree.or.kr/blog/web-development/javascript/script-%ED%83%9C%EA%B7%B8%EC%9D%98-async%EC%99%80-defer-%EC%86%8D%EC%84%B1/)) ## Progressive rendering Progressive rendering은 콘텐츠를 빠르게 화면에 렌더링하는 기법입니다. 브로드밴드 인터넷이 등장하기 전에 매우 중요한 기술이었고, 모바일 플랫폼을 고려한다면 여전히 무시할 수 없는 부분입니다. 가시영역의 이미지만 로딩해주는 jQuery 플러그인 Lazy loading이 좋은 예입니다. ([참고](https://stackoverflow.com/questions/33651166/what-is-progressive-rendering)) # CSS ## Reset CSS 모든 브라우저에서 통일된 화면을 볼 수 있도록 CSS의 기본값을 초기화하는 것을 말합니다. [Eric Meyer's Reset](https://meyerweb.com/eric/tools/css/reset/)과 [Normalize.css](https://necolas.github.io/normalize.css/)가 주로 쓰입니다. ## BFC(Block Formatting Context)와 IFC(Inline Formatting Context) 모든 HTML 요소는 사각형 박스 형태를 취하고 있습니다. 박스는 Box-model이라는 모델을 가지고 있습니다. CSS요소에는 display가 존재하는데, 이는 블록(Block)과 인라인(Inline) 두 가지 값을 가집니다. block은 블록 레벨 요소(Block-level elements)를 의미하며, 블록 레벨 요소는 BFC에 속하는 박스입니다. 블록 레벨 요소 박스는 수직으로 계속 쌓입니다. inline은 인라인 레벨 요소(Inline-level elements)를 의미합니다. 인라인 레벨 요소는 인라인 레벨 박스를 생성하며, 이는 IFC에 속합니다. 인라인 레벨 요소 박스는 수평으로 계속 쌓입니다. 또한 인라인 레벨 박스에 border나 padding이 눈에 보이더라도 사실은 line-height에 의해 높이가 조절됩니다. inline-block는 특이한데요, 이 요소는 인라인 요소처럼 수평으로 쌓이지만 블록 레벨 요소의 박스처럼 높이를 계산합니다. 즉 line-height에 의존하지 않습니다. ([참고](https://brunch.co.kr/@techhtml/21)) ## 클리어링(Clearing) 기술 클리어링는 float 속성이 주변 요소의 배치에 영향을 미치지 않도록하는 것입니다. float 속성을 가진 요소는 자신의 위치를 주변 콘텐츠로부터 상대적으로 배치하기 때문에 다른 콘텐츠가 그 주위로 흐르게 됩니다. 클리어링를 통해 이를 방지할 수 있는데요, 여기에는 4가지 방법이 있습니다. 첫 번째는 float에 float으로 대응하는 것입니다. 자식 요소 뿐만 아니라 부모 요소에게도 float 속성을 지정하면 부모 요소가 자식 요소의 높이를 반영합니다. 단, 이렇게 하면 부모 요소의 너비가 자식 요소를 담을 정도로 줄어듭니다. 두 번째는 float에 overflow 속성으로 대응하는 것입니다. 자식 요소의 높이를 부모에게 반영하는 방법으로, 부모 요소의 overflow 속성에 auto 또는 hidden 값을 부여합니다. 하지만 auto 값의 경우 자식 요소가 부모 요소보다 클 때 스크롤바가 생기며, hidden 값의 경우 넘치는 부분이 잘려버립니다. 세 번째로 float을 빈 엘리먼트로 클리어링할 수도 있습니다. float이 지정된 요소 뒤에 빈 요소를 추가하고 빈 요소의 clear 속성에 both 값을 부여하는 것인데, 의미없는 요소를 사용하게 되기 때문에 권장하지 않습니다. 마지막으로 float을 가상 선택자 `:after`로 클리어링하는 방법이 있습니다. 가상 선택자는 가상 클래스(pseudo-class)와 가상 요소(pseudo-element)로 나뉩니다. 가상 클래스는 특정 요소에 아무런 class를 부여하지 않았지만 마치 class를 변경한 것과 같은 역동적인 효과를 낼 수 있는 것들입니다. `:hover`, `:active`, `:focus` 등이 여기에 속합니다. 가상 요소는 존재하지 않는 요소를 가상으로 생성하는 선택자입니다. `:first-line`, `:before`, `:after`가 여기에 속합니다. 가상 요소는 HTML 문서에 존재하지 않는 콘텐츠를 출력하기도 합니다. 이렇게 가상 요소를 생성한 다음 `display: block; clear: both;` 처리를 하면 깔끔하게 클리어링을 할 수 있습니다. 가장 권장하는 방법입니다. ([참고](http://naradesign.net/wp/2008/05/27/144/)) ## Image Replacement 요소를 이미지로 교체하는 기법입니다. 기본적으로 다음과 같이 하면 됩니다: .elements { background-image: url("image.png"); } 그리고 [A History of CSS Image Replacement](https://www.sitepoint.com/css-image-replacement-text-indent-negative-margins-and-more/)에서 더 많은 예시를 볼 수 있습니다. ## IE box model과 W3C box model의 차이 브라우저에 따라 박스 모델이 달라 요소에 지정된 너비와 높이가 같아도 서로 다르게 렌더링됩니다. 박스 모델은 기본 적으로 margin, border, padding, content로 구성되어 있습니다. ![Box model](https://cloud.githubusercontent.com/assets/11814588/9597163/cb8fdf6a-50b9-11e5-80f0-da960d3f88cc.gif) W3C의 표준 박스 모델은 콘텐츠의 너비, 높이만을 width와 height로 계산하는 반면, IE의 박스 모델은 콘텐트와 padding, border를 포함한 너비와 높이를 width, height로 계산합니다. 이를 위한 해결책은 (1)DTD를 통해 브라우저가 쿽스 모드로 동작하지 않도록 하거나 (2)wrapper 요소를 사용해 wrapper 요소에 width, height 값을 할당하고 내부 엘리먼트에 padding, border 값을 할당하거나 (3)Conditional comment를 추가하거나 (3)css hack을 사용하는 방법이 있습니다. ([참고](https://github.com/nhnent/fe.javascript/wiki/%EB%B0%95%EC%8A%A4%EB%AA%A8%EB%8D%B8)) ## 그리드 시스템 (Grid system) 반응형 웹페이지를 만들기 위해 자주 쓰이는 기술입니다. 페이지 위에 격자를 그리고 그 위에 요소를 그 위에 배치하는 방법으로, 기술적인 이유만이 아니라 디자인적인 이유로도 사용합니다. 보통 그리드 시스템을 위해 [부트스트랩](http://bootstrapk.com/css/#grid)을 사용합니다. ## CSS 전처리기 CSS 문서가 방대해짐에 따라 작업 효율을 높이기 위해 등장한 기술입니다. 전처리기를 사용하면 CSS 상의 반복적인 부분을 스크립트나 변수로 처리할 수 있고, 다양한 연산이 가능해집니다. Less나 Sass가 대표적인 CSS 전처리기입니다. @bgColor: #DFDFDF; body { background-color: @bgColor; } 위는 Less의 예시입니다. 컴파일을 하면 body의 background-color 값이 #DFDFDF로 치환된 CSS 코드를 얻을 수 있습니다. ## 반응형 디자인(Responsive design)과 적응형 디자인(Adaptive design) 반응형 디자인은 디스플레이의 너비에 따라 레이아웃을 변형시키고, 적응형 디자인은 고정적 레이아웃을 가집니다. 반응형웹이 미디어쿼리를 사용해 스타일 분기를 나누는 방법이라면 적응형 웹은 디바이스를 체크해 그 디바이스에 최적화된 마크업을 호출하는 방법입니다. ([참고](https://studio-jt.co.kr/%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A0%81%EC%9D%91%ED%98%95-%EC%9B%B9/)) # Javascript ## 추상적 같음 비교(Abstract equality comparison)와 엄격한 같음 비교(Strict equality comparison) 추상적 같음 비교(`==`)는 두 변수를 같은 데이터 타입으로 변환한 다음 값을 비교하는 반면, 엄격한 같음 비교(`===`)는 두 변수의 값과 데이터 타입을 함께 비교합니다. 따라서 값과 타입이 완전히 일치해야 true를 반환합니다. 엄격한 같음 비교를 사용한 비교 결과는 예측이 쉽고 타입 강제가 일어나지 않기 때문에 추상적 같음 비교를 사용하는 것보다 낫습니다. ([참고](https://developer.mozilla.org/ko/docs/Web/JavaScript/Equality_comparisons_and_sameness)) ## 고급 예외처리(Advanced exception handling) `try...catch`와 `throw`로 고급 예외처리를 할 수 있습니다. 참고로 프로그램 실행 중에 발생하는 오류는 예외(Exception)이라고 하며, 코드의 문법적 오류는 에러(Error)라고 한다. `try...catch`는 구동 중 코드상에서 발생할 수 있는 오류들을 잡아준다. try { // try 블록은 예외가 발생할 수도 있는 부분 } catch(e) { // catch 블록은 try 블록에서 예외가 발생했을 때 호출되는 부분 // 객체 e는 name과 message 속성을 가짐 } finally { // 선택적으로 finally 블록을 추가할 수 있음 // 예외 발생 여부를 떠나 무조건 실행되어야 할 부분 } `throw`문은 강제로 예외를 발생시킬 때 사용합니다. `throw`의 문법은 다음과 같습니다: throw expression; 표현식의 값은 숫자, 문자 등 어떤 것이든 상관없습니다. `throw`가 발생하면 가장 가까운 `catch` 블록으로 이동합니다. 다음과 같이 사용하면 됩니다: try { if(a > 100) { throw "Value too high"; } } catch(e) { alert(e); } ([참고1](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#예외처리문)) ([참고2](http://webclub.tistory.com/71)) ## Event delegation 이벤트 리스너를 등록하는 것은 DOM 입장에서는 부담스러운 일이며, 리소스를 상당히 잡아먹는 작업입니다. 그래서 요소 하나하나에 이벤트를 등록하는 대신 위임을 하는 것이 바람직합니다. 자식 요소들을 감싼 부모 요소에 이벤트를 등록해 이벤트를 위임하면 각각의 자식 요소들에 이벤트를 등록하는 효과를 낼 수 있습니다. 이벤트를 위임하면 리소스를 절약할 수 있을 뿐만 아니라 DOM 트리에 새로운 요소를 추가했을 때 코드를 추가할 필요가 없어지고, 코드의 양을 줄일 수도 있습니다. 다음은 ul 요소의 자식 요소인 li 요소를 클릭하면 해당 li 요소가 사라지는 코드입니다. <ul id="todoList"> <li>TODO: A</li> <li>TODO: B</li> <li>TODO: C</li> </ul> 아주 평범한 리스트입니다. function getTarget(e) { if(!e) { // event 객체가 존재하지 않으면 e = window.event; // IE의 event 객체를 사용 } return e.target || e.srcElement; // 이벤트가 발생한 요소를 가져옴 } function itemDone(e) { var elTarget, elParent; elTarget = getTarget(e); // 이벤트가 발생한 요소 가져옴 (li) elParent = target.parentNode; // 해당 요소의 부모 요소를 가져옴 (ul) elParent.removeChild(elTarget); // 이벤트가 발생한 요소를 제거함 (li) } (function(){ var el = document.getElementById('todoList'); if(el.addEventListener) { // 이벤트 리스너가 지원되면 el.addEventListener('click', function(e) { // 클릭 이벤트에 리스너를 지정 itemDone(e); }, false); // 이벤트 버블링을 사용 } else { // 이벤트 리스너가 지원되지 않으면 el.attachEvent('onclick', function(e) { // IE의 onclick 이벤트를 사용 itemDone(e); } }); })(); IE까지 고려한 코드입니다. 하나의 이벤트 리스너로 요소 3개를 제어하고 있습니다다. jQuery는 보다 편하게 이벤트를 바인딩할 수 있도록 [`.delegate()`](http://api.jquery.com/delegate/) 메소드를 제공하고 있습니다. ## Prototype 기반 상속 자바스크립트는 클래스라는 개념이 없기때문에 프로토타입(Prototype)이 클래스를 대신해 객체지향 프로그래밍의 핵심을 맡습니다. (ES6부터 클래스 문법이 추가됐기 때문에 프로토타입에 직접 접근할 필요가 없어졌습니다. 그래도 프토로타입은 자바스크립트의 기반이기 때문에 알아두는 것이 좋습니다.) 자바스크립트에서는 이 프로토타입을 통해 다른 객체지향 언어에서 쓰이는 클래스를 구현할 수 있습니다. function Player() { this.hp = 100; this.mp = 50; } var kim = new Player(); var park = new Player(); console.log(kim.hp); // 100 console.log(kim.mp); // 50 console.log(park.hp); // 100 console.log(park.mp); // 50 `kim`과 `park`은 `hp`와 `mp`를 각각 100, 50씩 가지고 있습니다. 만약 객체를 100개 만들면 200개의 변수가 메모리에 할당됩니다. 프로토타입을 활용하면 메모리를 아낄 수 있습니다. function Player() {} Player.prototype.hp = 100; Player.prototype.mp = 50; var kim = new Player(); var park = new Player(); `kim`과 `park`은 프로토타입에 연결된 `Player` 객체의 값을 가져다 쓸 수 있습니다. `hp`와 `mp`를 `kim`과 `park`이 공유하는 것입니다. 이러한 매커니즘으로 프로토타입을 이용해 상속을 구현할 수 있습니다. ([참고1](https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67)) ([참고2](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain)) ## null, undefined, undeclared의 차이 이건 종종 헷갈리는 경우가 있습니다. null은 어떠한 값도 가리키지 않는다는 의미의 원시값입니다. 변수를 선언하고 null을 할당한 경우 해당 변수가 어떠한 값도 가지지 않았다는 의미가 됩니다. undefined는 변수가 선언됐지만 값이 할당되지 않았다는 것을 의미하는 값입니다. 즉, null은 개발자로부터 할당되는 값이고, undefined는 아예 할당을 하지 않은 상태입니다. undeclared는 변수 자체가 선언되지 않았다는 의미입니다. 콘솔에서는 undefined와 똑같이 표기되기 때문에 변수가 초기화되지 않았다는 것인지, 아예 선언되지 않았다는 것인지 확인할 필요가 있습니다. ## 클로저(Closure) 자바스크립트에서는 함수 안에 또 다른 함수를 선언할 수 있는데, 함수 안의 함수인 내부함수(inner function)는 외부함수(outer function)의 지역변수에 접근할 수 있습니다. 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수는 외부함수에 접근할 수 있습니다. 이러한 메커니즘을 클로저라고 합니다. MDN에 클로저를 활용한 재밌는 예제가 있습니다: function makeAdder(x) { return function(y) { return x + y; }; } var add5 = makeAdder(5); var add10 = makeAdder(10); console.log(add5(2)); // 7 console.log(add10(2)); // 12 여기서 `makeAdder(x)`는 `x`를 인자로 받아서 새로운 함수를 반환합니다. 그리고 반환되는 내부 함수는 `y`를 인자로 받아 `x + y`를 반환합니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures)) ([참고2](https://opentutorials.org/course/743/6544)) ## 자바스크립트 모듈 패턴(Module pattern) 모듈 패턴(Module pattern)은 코드 설계 방법을 말합니다. 여기서는 객체를 public과 private으로 나누는 캡슐화가 핵심입니다. 자바스크립트는 public이나 private와 같은 접근 제한자를 제공하지 않지만, 클로저를 이용해 구현할 수 있습니다. private method는 코드 접근을 제한할 수 있을뿐만 아니라 추가적인 자바스크립트가 다른 스크립트와 이름이 충돌하는 것을 막을 수 있습니다. 매우 기본적인 방법은 모든 코드를 익명함수 안에 집어넣어 private 스코프로 만드는 것입니다. 하지만 이렇게 하면 코드를 재사용하기 불편해지기 때문에 별도의 네임스페이스를 적용해야 합니다. ([참고1](http://blog.javarouka.me/2012/02/javascripts-pattern-2-module-pattern.html)) ([참고2](http://asfirstalways.tistory.com/234) ## 네이티브 객체(Native object), 호스트 객체(Host object), Built-in 객체 객체는 크게 3가지로 구분됩니다. 네이티브 객체는 ECMAScript 명세에 정의된 객체로, 자바스크립트의 모든 엔진에 구현된 표준객체입니다. BOM(Browser Object Model)과 DOM 등은 모두 네이티브 객체며, 자바스크립트 엔진을 구동하는 측에서 빌드되는 객체입니다. 호스트 객체는 개발자가 정의한 객체입니다. 마지막으로 Built-in 객체는 자바스크립트 엔진을 구성하는 기본 객체들을 포함합니다. Number, String, Array, Date 등의 내장객체들이 있습니다. ([참고](https://github.com/hckoo/javascriptstudy2013/blob/master/1209/6%EC%9E%A5%20%EA%B0%9D%EC%B2%B4.md)) ## 기능 검출(Feature detection)과 기능 추론(Feature inference) 기능 검출이란 스크립트가 호출하는 기능을 사용자의 브라우저가 지원하는지 체크하는 것을 말합니다. 다음은 브라우저가 GPS를 지원하는지 확인하는 코드입니다. if(navigator.geolocation) {...} 기능 추론도 기능 검출처럼 브라우저가 특정 기능을 지원하는지 체크하는 것입니다. 하지만 'A기능을 지원하면 B기능도 지원할 것이다.'라는 추론이 바탕이 됩니다. 별로 좋지 않은 방법입니다. ([참고](https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th)) ## 호이스팅(Hoisting) 호이스팅은 인터프리터가 스크립트를 해석할 때, 변수의 정의가 스코프에 따라 선언과 할당으로 분리되어 선언을 항상 컨텍스트의 최상위로 끌어올리는 것을 의미합니다. 즉, 변수를 어디에서 선언하든 인터프리터는 최상단에서 선언한 것으로 해석합니다. 이는 변수 뿐 아니라 함수에도 적용됩니다. 따라서 상단에서 함수를 호출하고, 하단에서 함수를 정의해도 기능적인 문제는 없습니다. ([참고](https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th)) ## 이벤트 흐름(Event flow) HTML 요소가 다른 요소의 내부에 중첩되어 있을 때 자식 요소를 클릭하면 부모 요소를 클릭한 셈이 됩니다. 이처럼 이벤트는 흐름을 가지고 있으며, 이것을 이벤트 흐름이라고 부릅니다. 이벤트 흐름에는 두 가지 방식이 있습니다. 먼저 이벤트 버블링(Event bubbling)은 이벤트가 직접적으로 발생한 노드로부터 시작해 바깥 노드로 이벤트가 퍼져 나가는 방식을 말합니다. 대부분의 브라우저가 기본적으로 이 방식을 지원합니다. 반대로 이벤트 캡쳐링(Event capturing)은 바깥 노드부터 시작해서 안쪽으로 퍼지는 방식입니다. IE8 혹은 그 이전 버전에서는 지원되지 않습니다. ## document load event와 DOMContentLoaded event DOM을 제어하는 스크립트는 마크업의 모든 요소에 대한 처리가 끝난 뒤에 로드되어야 합니다. 그래서 보통 body 태그 최하단에서 스크립트를 불러오도록합니다. 또 다른 방법은 이벤트를 이용하는 것입니다. document load event는 페이지의 모든 리소스가 로드된 이후에 실행됩니다. 때문에 구동이 지연되어 사용자 경험을 저하할 수 있습니다. 반면 DOMContentLoaded event는 스크립트 로드를 마치고 실행이 가능한 시점에 바로 실행됩니다. ([참고](https://opentutorials.org/module/904/6765)) ## 조건부 삼항 연산자(Conditional ternary operator) 보통 그냥 '삼항 연산자'이라고 부릅니다. if문을 축약해서 쓸 수 있는 유용한 연산자이지만, 과하게 사용하면 코드의 가독성을 떨어뜨릴 수 있습니다. var a = 1, b = 2; console.log(a < b ? "True" : "False"); // logs "True" console.log(a > b ? "True" : "False"); // logs "False" a < b ? ( console.log("True"); alert("True"); ) : ( console.log("False"); alert("False"); ); // logs "True" 조건이 true면 전자의 값을 반환하고, false면 후자의 값을 반환합니다. ## use strict ES6에서 새로 추가된 기능으로, 스크립트에 `"use strict;"` 구문을 추가하면 strict mode에서 실행하게 됩니다. strict mode는 코딩 실수를 찾아 예외를 발생시키고, 전역 객체에 접근하는 것과 같은 위험한 액션을 막습니다. 스크립트 전체에 적용할 수도 있고 특정 함수에만 적용할 수도 있습니다. ([참고](https://stackoverflow.com/questions/1335851/what-does-use-strict-do-in-javascript-and-what-is-the-reasoning-behind-it)) ## Call stack과 Task queue 자바스크립트 엔진은 요청이 들어올 때마다 요청을 순차적으로 call stack에 담아 하나씩 처리합니다. call stack은 하나만 존재하기 때문에 요청도 하나씩만 처리할 수 밖에 없습니다. task queue는 처리해야 하는 task를 임시로 저장해두는 큐입니다. call stack이 비워지면 task queue에 있던 task들이 순서대로 call stack에 push됩니다. 가령 `setTimeout()` 함수로 10초의 딜레이를 둔다면, 그동안 `setTimeout()`이 처리할 task는 task queue에 쌓이고 다른 부분의 스크립트들이 실행됩니다. 따라서 딜레이를 0으로 줘도 `setTimeout()`의 task는 다른 것들보다 나중에 처리됩니다. ([참고](https://github.com/nhnent/fe.javascript/wiki/June-13-June-17,-2016))
json metadata{"tags":["front-end","development","web","kr","dfdfdf"],"users":["import"],"image":["https://cloud.githubusercontent.com/assets/11814588/9597163/cb8fdf6a-50b9-11e5-80f0-da960d3f88cc.gif"],"links":["https://github.com/h5bp/Front-end-Developer-Interview-Questions","https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation","https://stackoverflow.com/questions/3684923/javascript-variables-declare-outside-or-inside-loop","http://www.clearboth.org/51_graceful_degradation_versus_progressive_enhancement/","http://webdir.tistory.com/416","https://developer.mozilla.org/ko/docs/Web/Accessibility/ARIA","http://www.bloter.net/archives/148889","https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy","https://brunch.co.kr/@99-life/2","http://www.internetmap.kr/entry/Crossorigin-resource-sharing","http://webdir.tistory.com/40","https://developer.mozilla.org/ko/docs/Web/HTML/Quirks_Mode_and_Standards_Mode","http://blog.wystan.net/2007/05/24/xhtml-vs-html","https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/data-*","https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies","https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage","https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage","https://appletree.or.kr/blog/web-development/javascript/script-%ED%83%9C%EA%B7%B8%EC%9D%98-async%EC%99%80-defer-%EC%86%8D%EC%84%B1/","https://stackoverflow.com/questions/33651166/what-is-progressive-rendering","https://meyerweb.com/eric/tools/css/reset/","https://necolas.github.io/normalize.css/","https://brunch.co.kr/@techhtml/21","http://naradesign.net/wp/2008/05/27/144/","https://www.sitepoint.com/css-image-replacement-text-indent-negative-margins-and-more/","https://github.com/nhnent/fe.javascript/wiki/%EB%B0%95%EC%8A%A4%EB%AA%A8%EB%8D%B8","http://bootstrapk.com/css/#grid","https://studio-jt.co.kr/%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A0%81%EC%9D%91%ED%98%95-%EC%9B%B9/","https://developer.mozilla.org/ko/docs/Web/JavaScript/Equality_comparisons_and_sameness","https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#예외처리문","http://webclub.tistory.com/71","http://api.jquery.com/delegate/","https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67","https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain","https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures","https://opentutorials.org/course/743/6544","http://blog.javarouka.me/2012/02/javascripts-pattern-2-module-pattern.html","http://asfirstalways.tistory.com/234","https://github.com/hckoo/javascriptstudy2013/blob/master/1209/6%EC%9E%A5%20%EA%B0%9D%EC%B2%B4.md","https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th","https://opentutorials.org/module/904/6765","https://stackoverflow.com/questions/1335851/what-does-use-strict-do-in-javascript-and-what-is-the-reasoning-behind-it","https://github.com/nhnent/fe.javascript/wiki/June-13-June-17,-2016"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfront-end
permlink4dkun
title프론트엔드 개발자를 위한 토막상식
Transaction InfoBlock #19822720/Trx d0440e04030eb07d5edb41774d93e7c64d026c1a
View Raw JSON Data
{
  "block": 19822720,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "프로젝트하면서 알게 된 것들과 코딩테스트를 통해 배운 것들, 자바스크립트&제이쿼리: 인터랙티브 프론트엔드 웹 개발 교과서(Jon Duckett)와 JavaScript: The Good Parts(Douglas Crockford)를 통해 공부한 것들, 그리고 [Front-end Job Interview Questions](https://github.com/h5bp/Front-end-Developer-Interview-Questions)에 나와있는 질문들에 대한 대답을 간략하게 정리했습니다. 정보의 출처는 글마다 참고 링크를 걸어두었습니다. 잘못된 내용이 있다면 댓글로 달아주세요!\n\n# 일반적인 것들\n\n## 헝가리안 표기법\n\n변수 이름에 데이터 타입을 접두어로 붙이는 표기법입니다. 숫자라면 nVar, 배열이라면 aVar 이런 식이으로 붙입니다. 2015년 네이버에서 인턴십을 할 때 코딩컨벤션이 헝가리언 표기법 + 캐멀케이스여서 그때 처음 접하고 사용하기 시작했는데, 요즘에는 헝가리언 표기법을 지양한다고 합니다. 가독성과 유지보수 효율성이 떨어진다네요. ([참고](https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation))\n\n## 블록 안에서 변수 선언\n\nJava의 경우 한 블록 안에서만 쓰이는 변수를 코드 위쪽에 선언하지 않고 블록 안에 변수를 선언하면 코드를 파악하기 더욱 쉬워지고, 스코프를 제한할 수 있어 안전합니다. 자바스크립트의 `var`타입은 블록 스코프가 아닌 함수 스코프를 적용하기 때문에 어디에 선언하든 아무런 차이가 없습니다. 하지만 `let`이나 `const`를 사용한다면 얘기가 달라집니다. 이 둘은 블록 스코프입니다. ([참고](https://stackoverflow.com/questions/3684923/javascript-variables-declare-outside-or-inside-loop))\n\n## 점진적 향상법(Progressive enhancement)과 우아한 성능저하법(Graceful degradation)\n\n사용자가 항상 최신 기술을 사용할 수 있는 환경에서 서비스를 사용하지는 않습니다. 특히 웹사이트를 만들 때는 최신 브라우저와 구식 브라우저를 모두 신경써야 합니다. 점진적 향상법과 우아한 성능저하법은 최신과 구식에 대응하기 위한 방법을 말합니다. ([참고](http://www.clearboth.org/51_graceful_degradation_versus_progressive_enhancement/))\n\n점진적 향상법은 기본적으로 구식 기술 환경에서 동작할 수 있는 기능을 구현하고, 최신 기술을 사용할 수 있는 환경에서는 더 나은 사용자 경험을 제공할 수 있는 최신 기술을 제공하는 방법입니다. 즉, 구식 환경에서도 충분히 서비스를 사용할 수 있고, 최신 환경이라면 더 나은 기능들을 사용할 수 있도록 만드는 것입니다. 구식 브라우저를 사용하는 사용자에게 100만큼의 기능을 제공하고, 최신 브라우저를 사용하는 사용자에게는 130정도의 기능을 제공하도록 웹사이트를 만든다고 보면 됩니다.\n\n우아한 성능저하법은 점진적 향상법과 반대입니다. 이는 최신 기술을 기반으로 기능을 구현한 뒤, 구형 기술에 기반한 환경에서도 유사하게 동작하도록 만드는 방법입니다. 최신 브라우저에서는 100만큼의 기능을 제공하고, 구식 브라우저에서는 50정도의 기능만을 제공하게 웹사이트를 만드는 것입니다. img 태그에 alt 속성을 지정함으로써 이미지를 보여주지 못하는 환경에서 이미지를 텍스트로 대체하는 것이 대표적인 예시입니다.\n\n## FOUC(Flash Of Unstyled Content)\n\nFOUC는 외부의 CSS 코드를 불러오기 전 스타일이 적용되지 않은 페이지가 잠시 나타나는 현상입니다. 특히 IE에서 자주 발생하는 현상으로, 사옹자 경험을 저하하는 요인이 됩니다. FOUC가 발생하는 주요 원인은 브라우저가 마크업에 참조된 파일들을 모아 DOM을 생성할 때 가장 빠르게 분석할 수 있는 부분(HTML)을 먼저 화면에 표시한 뒤 화면에 출력된 마크업 순서에 따라 스타일(CSS)을 적용하고 스크립트(Javascript)를 실행하기 때문입니다.\n\nFOUC를 해결하기 위해서는 head 태그 안에 CSS를 링크하고, @import의 사용을 자제해야 합니다. 또한 자바스크립트를 head 태그 안에서 로드하는 것도 방법될 수 있습니다. (하지만 별로 추천하지 않습니다.) 어떤 방법으로도 해결되지 않는다면 FOUC가 발생하는 구역을 숨겼다가 브라우저가 준비됐을 때 다시 보여주는 방법이 있습니다. ([참고](http://webdir.tistory.com/416))\n\n## ARIA(Accessible Rich Internet Applications)와 스크린리더\n\nARIA는 접근가능한 인터넷 어플리케이션을 의미합니다. 이는 웹 콘텐츠를 개발할 때 장애인을 위한 접근성 향상 방법을 정의합니다. ARIA를 사용해 웹사이트여러 곳의 접근성을 향상할 수 있습니다. ARIA는 html 태그에 role 속성을 지정하는 방식으로 사용할 수 있으며, 대부분의 브라우저와 스크린리더가 ARIA를 지원하고 있습니다. 스크린리더는 웹사이트의 구성 요소들을 읽어주는 프로그램으로, 시각장애를 가진 사용자가 컴퓨터 화면에 무엇이 있는지 인지할 수 있게 돕습니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/Accessibility/ARIA)) ([참고2](http://www.bloter.net/archives/148889))\n\n## CSS 애니메이션과 Javascript 애니메이션의 차이\n\n자바스크립트는 메인 쓰레드가 무거운 작업을 하고 있을 때 애니메이션 처리의 우선순위를 미뤄두는 반면 CSS는 독립적인 쓰레드가 애니메이션을 처리해줍니다. 때문에 CSS 애니메이션은 최적화가 쉽습니다. 하지만 항상 CSS 애니메이션이 우수한 것은 아닙니다. UI 요소가 작은 경우 CSS를 사용하고, 애니메이션을 세밀하게 제어해야 하는 경우 자바스크립트를 사용하는 것이 좋습니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy)) ([참고2](https://brunch.co.kr/@99-life/2))\n\n## 동일출처정책(Same-origin policy)과 CORS(Cross-origin resource sharing)\n\n동일출처정책은 한 출처의 문서가 다른 출처의 문서와 상호작용하지 못하도록하는 정책입니다. 두 문서의 프로토콜, 포트, 호스트가 같으면 동일출처라고 합니다. CORS는 어떤 웹페이지에 있는 자바스크립트가 해당 도메인이 아닌 다른 도메인에 XMLHttpRequests 요청을 허용하는 기법을 말합니다. 브라우저는 서버와 통신할 때마다 서로에 대한 정보를 HTTP 헤더를 이용해 전달합니다. CORS는 HTTP 헤더에 정보를 추가해 브라우저와 서버가 서로 통신해야 한다는 사실을 알게 합니다. CORS를 통하지 않을 경우, Cross-domain 요청은 동일출처정책에 의해 브라우저가 금지합니다. CORS는 다른 출처에서 온 요청을 허용할 지 결정하기 위해 브라우저와 서버가 상호교류하는 방법을 정의한 것입니다. CORS는 W3C 명세에 포함되어 있지만 IE의 경우에는 비표준 XDomainRequest 객체를 사용하여 CORS 요청을 처리합니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy)) ([참고2](http://www.internetmap.kr/entry/Crossorigin-resource-sharing))\n\n## 코드 의존성과 DRY원칙\n\n어떤 스크립트는 다른 추가적인 스크립트를 필요로 하기도 합니다. 이때 다른 스크립트에 의존하는 스크립트를 작성할 때 해당 스크립트에 의존성이 있다고 표현합니다. jQuery를 활용하는 스크립트는 의존성이 있다고 할 수 있습니다. 이런 경우 주석을 통해 의존성을 명시하는 것이 좋습니다.\n\nDRY원칙은 Don't Repeat Yourself의 줄임말로, 소프트웨어 개발 원칙을 말합니다. 한국어로는 중복배제라고 합니다. 같은 작업을 수행하는 코드를 두 번 작성했다는 이를 코드 중복이라고 하는데, DRY원칙은 이러한 코드 중복을 지양하자는 원칙입니다. 코드 중복의 반대 개념으로는 코드 재사용(Code reuse)이 있습니다. 코드 재사용은 같은 코드가 스크립트의 다른 곳에서 한 번 이상 사용되는 것을 말합니다. 함수를 활용하는 것은 코드 재사용의 좋은 예로, 재사용 가능한 함수를 헬퍼 함수(Helper function)라고 합니다. 코드 재사용을 권장하기 위해 개발자들은 작은 스크립트를 작성하는데, 이때문에 코드 재사용은 코드 사이에 더 많은 의존성을 만듭니다.\n\n# HTML\n\n## DOCTYPE\n\nDOCTYPE은 문서형을 말하며, 해당 문서가 어떤 버전의 어떤 마크업 언어로 구성되어있는지를 의미합니다. DOCTYPE을 선언하는 것을 DTD(Dodumenttation Type Declaration)라고 한다. DTD는 각 마크업의 각 버전에서 사용가능한 태그나 속성 등을 정의하기 때문에 문서의 최상단에 위치해야 합니다. HTML5 이전 버전은 SGML(Standard Generalized Markup Language)에 기반했기 때문에 DTD 참조가 필수적입니다. HTML5는 DTD 참조가 필요하지 않으며, 하위 호환을 위해 `<!DOCTYPE html>`만으로 선언합니다. ([참고](http://webdir.tistory.com/40))\n\n## 표준모드(Standard mode)와 호환모드(Quirks mode)\n\n표준모드와 호환모드는 브라우저가 가진 두 가지 렌더링 모드입니다. 브라우저는 DTD에 따라 렌더링할 모드를 선택하는데, 이 과정을 Doctype sniffing또는 Doctype switching이라고 합니다. 브라우저가 출력하고자 하는 문서가 최신이라면 W3C나 IETF의 표준을 엄격히 준수하는 표준모드로 렌더링을 합니다. 반면 문서가 오래된 버전이라면 호환모드로 렌더링합니다. 호환모드는 이전 세대의 브라우저에 맞는 비표준 규칙을 문서에 적용해 오래된 웹페이지들이 최신 브라우저에서 깨져보이지 않게 합니다. 브라우저는 문서가 최신인지 아닌지를 DTD로 판단한다. 만약 DTD가 존재하지 않거나 일부가 누락된 경우 호환모드로 문서를 해석합니다. 또한 IE의 경우 DTD 앞에 주석이나 다른 문자가 들어갔을 때도 문서를 호환모드로 해석한다. ([참고](https://developer.mozilla.org/ko/docs/Web/HTML/Quirks_Mode_and_Standards_Mode))\n\n## XML과 XHTML\n\nXML과 XHTML모두 웹 문서 규격입니다. XML은 W3C에서 여러 특수 목적의 마크업 언어를 만드는 용도에서 권장되는 다목적 마크업 언어입니다. XML은 문서 상의 데이터 이름과 값 등을 구분하기 위해 만들어졌는데요, XML은 SGML(참고로 SGML은 인터넷이 등장하기 이전에 만들어졌다.)을 기반으로한 HTML의 한계를 극복하여 XHTML을 이끌었습니다. XML을 기반으로 한 HTML을 만든 셈입니다. XHTML은 XML의 문법을 따르며, HTML 문법과 매우 유사하지만 더 엄격합니다.\n\nXHTML이 더 표준인 것처럼 보이지만 사실 그렇게 권장하지 않는 사람들도 있습니다. 일단 HTML의 호환성이 더 높기 때문입니다. XHTML은 1.1버전부터 비표준이나 비권장 태그를 호환하지 못하게 되면서 지나치게 엄격하다는는 비판을 받았습니다. IE가 XHTML을 해석하지 못하는 것 역시 XHTML의 호환성을 떨어뜨리는 요인이 됐습니다. 또한 XHTML과 HTML의 요소와 속성에는 차이가 거의 없습니다. 단지 HTML에서는 `<br>`로 써도 되지만 XHTML에서는 반드시 `<br/>`로 써야한다는 정도의 문법이 다를 뿐입니다. 이런 점에서 굳이 XHTML을 써야할 기술적 이유는 없다는 것입니다. 한편 HTML5가 발표되면서 XHTML은 거의 사용되지 않고 있습니다. ([참고](http://blog.wystan.net/2007/05/24/xhtml-vs-html))\n\n## data-* 속성\n\nHTML5에서 새로 추가된 data- _속성은 커스텀 데이터 속성으로, 개발자가 임의로 이름을 붙일 수 있는 속성입니다. data-_ 속성은 html 태그 상에서 별다른 작용을 하지 않습니다. 자바스크립트가 DOM의 데이터에 접근하거나 서버에서 받아온 데이터를 활용해야 할 때 사용됩니다. ([참고](https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/data-*))\n\n## Cookie, sessionStorage, localStorage\n\n쿠키(Cookie), 세션 저장소(sessionStorage), 로컬 저장소(localStorage)는 브라우저에 데이터를 저장하기 위한 공간들입니다. HTML5 이전에는 쿠키를 주로 사용했습니다. 하지만 쿠키는 많은 양의 데이터를 저장할 수 없고, 동일한 도메인에 페이지를 요청할 때마다 서버로 함께 전송되며, 변조가 쉬워 보안이 취약합니다. 그래서 HTML5부터는 저장소 객체(Storage object)를 정의하고 있습니다. 저장소 객체는 세션 저장소와 로컬 저장소 두 가지를 제공합니다. 로컬 저장소에 데이터를 자장하면 창이나 탭을 닫아서 세션이 종료돼도 데이터가 보존되고, 열려 있는 모든 창이나 탭이 데이터를 공유하게 됩니다. 세션 저장소는 반대입니다. 일반적으로 브라우저는 저장소에 5mb 정도의 공간을 할당하며, 데이터는 키-값 쌍(KVP; Key-Value Pair)을 이용하는 저장소 객체의 속성으로 저장됩니다. 또한 브라우저는 데이터를 보호하기 위해 동일출처정책에 의거, 서로 다른 페이지는 같은 도메인에 저장된 데이터에만 접근이 가능하도록 제한하고 있습니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies)) ([참고2](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) ([참고3](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage))\n\n## script 태그의 async와 defer\n\nscript 태그에 async 속성과 defer 속성이 추가된 것은 HTML5부터입니다. 브라우저가 웹 문서에서 외부 스크립트를 불러오는 script 태그를 만나면 해당 스크립트를 내려받아 해석하고 실행할 때까지 HTML 코드 파싱 작업을 뒤로 미룹니다. 그래서 무거운 스크립트 문서를 해석할 때는 페이지 전체가 느려지는 현상이 발생합니다. 그런데 script 태그에 async나 defer 속성을 지정하면 마크업 코드 작업을 중단하지 않고 스크립트를 동시에 내려받게 됩니다. defer는 마크업 파싱을 마친 다음 스크립트를 실행하며, async는 스크립트를 내려받는 즉시 스크립트를 실행합니다. ([참고](https://appletree.or.kr/blog/web-development/javascript/script-%ED%83%9C%EA%B7%B8%EC%9D%98-async%EC%99%80-defer-%EC%86%8D%EC%84%B1/))\n\n## Progressive rendering\n\nProgressive rendering은 콘텐츠를 빠르게 화면에 렌더링하는 기법입니다. 브로드밴드 인터넷이 등장하기 전에 매우 중요한 기술이었고, 모바일 플랫폼을 고려한다면 여전히 무시할 수 없는 부분입니다. 가시영역의 이미지만 로딩해주는 jQuery 플러그인 Lazy loading이 좋은 예입니다. ([참고](https://stackoverflow.com/questions/33651166/what-is-progressive-rendering))\n\n# CSS\n\n## Reset CSS\n\n모든 브라우저에서 통일된 화면을 볼 수 있도록 CSS의 기본값을 초기화하는 것을 말합니다. [Eric Meyer's Reset](https://meyerweb.com/eric/tools/css/reset/)과 [Normalize.css](https://necolas.github.io/normalize.css/)가 주로 쓰입니다.\n\n## BFC(Block Formatting Context)와 IFC(Inline Formatting Context)\n\n모든 HTML 요소는 사각형 박스 형태를 취하고 있습니다. 박스는 Box-model이라는 모델을 가지고 있습니다. CSS요소에는 display가 존재하는데, 이는 블록(Block)과 인라인(Inline) 두 가지 값을 가집니다. block은 블록 레벨 요소(Block-level elements)를 의미하며, 블록 레벨 요소는 BFC에 속하는 박스입니다. 블록 레벨 요소 박스는 수직으로 계속 쌓입니다. inline은 인라인 레벨 요소(Inline-level elements)를 의미합니다. 인라인 레벨 요소는 인라인 레벨 박스를 생성하며, 이는 IFC에 속합니다. 인라인 레벨 요소 박스는 수평으로 계속 쌓입니다. 또한 인라인 레벨 박스에 border나 padding이 눈에 보이더라도 사실은 line-height에 의해 높이가 조절됩니다. inline-block는 특이한데요, 이 요소는 인라인 요소처럼 수평으로 쌓이지만 블록 레벨 요소의 박스처럼 높이를 계산합니다. 즉 line-height에 의존하지 않습니다. ([참고](https://brunch.co.kr/@techhtml/21))\n\n## 클리어링(Clearing) 기술\n\n클리어링는 float 속성이 주변 요소의 배치에 영향을 미치지 않도록하는 것입니다. float 속성을 가진 요소는 자신의 위치를 주변 콘텐츠로부터 상대적으로 배치하기 때문에 다른 콘텐츠가 그 주위로 흐르게 됩니다. 클리어링를 통해 이를 방지할 수 있는데요, 여기에는 4가지 방법이 있습니다.\n\n첫 번째는 float에 float으로 대응하는 것입니다. 자식 요소 뿐만 아니라 부모 요소에게도 float 속성을 지정하면 부모 요소가 자식 요소의 높이를 반영합니다. 단, 이렇게 하면 부모 요소의 너비가 자식 요소를 담을 정도로 줄어듭니다.\n\n두 번째는 float에 overflow 속성으로 대응하는 것입니다. 자식 요소의 높이를 부모에게 반영하는 방법으로, 부모 요소의 overflow 속성에 auto 또는 hidden 값을 부여합니다. 하지만 auto 값의 경우 자식 요소가 부모 요소보다 클 때 스크롤바가 생기며, hidden 값의 경우 넘치는 부분이 잘려버립니다.\n\n세 번째로 float을 빈 엘리먼트로 클리어링할 수도 있습니다. float이 지정된 요소 뒤에 빈 요소를 추가하고 빈 요소의 clear 속성에 both 값을 부여하는 것인데, 의미없는 요소를 사용하게 되기 때문에 권장하지 않습니다.\n\n마지막으로 float을 가상 선택자 `:after`로 클리어링하는 방법이 있습니다. 가상 선택자는 가상 클래스(pseudo-class)와 가상 요소(pseudo-element)로 나뉩니다. 가상 클래스는 특정 요소에 아무런 class를 부여하지 않았지만 마치 class를 변경한 것과 같은 역동적인 효과를 낼 수 있는 것들입니다. `:hover`, `:active`, `:focus` 등이 여기에 속합니다. 가상 요소는 존재하지 않는 요소를 가상으로 생성하는 선택자입니다. `:first-line`, `:before`, `:after`가 여기에 속합니다. 가상 요소는 HTML 문서에 존재하지 않는 콘텐츠를 출력하기도 합니다. 이렇게 가상 요소를 생성한 다음 `display: block; clear: both;` 처리를 하면 깔끔하게 클리어링을 할 수 있습니다. 가장 권장하는 방법입니다. ([참고](http://naradesign.net/wp/2008/05/27/144/))\n\n## Image Replacement\n\n요소를 이미지로 교체하는 기법입니다. 기본적으로 다음과 같이 하면 됩니다:\n\n    .elements {\n      background-image: url(\"image.png\");\n    }\n\n그리고 [A History of CSS Image Replacement](https://www.sitepoint.com/css-image-replacement-text-indent-negative-margins-and-more/)에서 더 많은 예시를 볼 수 있습니다.\n\n## IE box model과 W3C box model의 차이\n\n브라우저에 따라 박스 모델이 달라 요소에 지정된 너비와 높이가 같아도 서로 다르게 렌더링됩니다. 박스 모델은 기본 적으로 margin, border, padding, content로 구성되어 있습니다.\n\n![Box model](https://cloud.githubusercontent.com/assets/11814588/9597163/cb8fdf6a-50b9-11e5-80f0-da960d3f88cc.gif)\n\nW3C의 표준 박스 모델은 콘텐츠의 너비, 높이만을 width와 height로 계산하는 반면, IE의 박스 모델은 콘텐트와 padding, border를 포함한 너비와 높이를 width, height로 계산합니다. 이를 위한 해결책은 (1)DTD를 통해 브라우저가 쿽스 모드로 동작하지 않도록 하거나 (2)wrapper 요소를 사용해 wrapper 요소에 width, height 값을 할당하고 내부 엘리먼트에 padding, border 값을 할당하거나 (3)Conditional comment를 추가하거나 (3)css hack을 사용하는 방법이 있습니다. ([참고](https://github.com/nhnent/fe.javascript/wiki/%EB%B0%95%EC%8A%A4%EB%AA%A8%EB%8D%B8))\n\n## 그리드 시스템 (Grid system)\n\n반응형 웹페이지를 만들기 위해 자주 쓰이는 기술입니다. 페이지 위에 격자를 그리고 그 위에 요소를 그 위에 배치하는 방법으로, 기술적인 이유만이 아니라 디자인적인 이유로도 사용합니다. 보통 그리드 시스템을 위해 [부트스트랩](http://bootstrapk.com/css/#grid)을 사용합니다.\n\n## CSS 전처리기\n\nCSS 문서가 방대해짐에 따라 작업 효율을 높이기 위해 등장한 기술입니다. 전처리기를 사용하면 CSS 상의 반복적인 부분을 스크립트나 변수로 처리할 수 있고, 다양한 연산이 가능해집니다. Less나 Sass가 대표적인 CSS 전처리기입니다.\n\n    @bgColor: #DFDFDF;\n    body {\n      background-color: @bgColor;\n    }\n\n위는 Less의 예시입니다. 컴파일을 하면 body의 background-color 값이 #DFDFDF로 치환된 CSS 코드를 얻을 수 있습니다.\n\n## 반응형 디자인(Responsive design)과 적응형 디자인(Adaptive design)\n\n반응형 디자인은 디스플레이의 너비에 따라 레이아웃을 변형시키고, 적응형 디자인은 고정적 레이아웃을 가집니다. 반응형웹이 미디어쿼리를 사용해 스타일 분기를 나누는 방법이라면 적응형 웹은 디바이스를 체크해 그 디바이스에 최적화된 마크업을 호출하는 방법입니다. ([참고](https://studio-jt.co.kr/%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A0%81%EC%9D%91%ED%98%95-%EC%9B%B9/))\n\n# Javascript\n\n## 추상적 같음 비교(Abstract equality comparison)와 엄격한 같음 비교(Strict equality comparison)\n\n추상적 같음 비교(`==`)는 두 변수를 같은 데이터 타입으로 변환한 다음 값을 비교하는 반면, 엄격한 같음 비교(`===`)는 두 변수의 값과 데이터 타입을 함께 비교합니다. 따라서 값과 타입이 완전히 일치해야 true를 반환합니다. 엄격한 같음 비교를 사용한 비교 결과는 예측이 쉽고 타입 강제가 일어나지 않기 때문에 추상적 같음 비교를 사용하는 것보다 낫습니다. ([참고](https://developer.mozilla.org/ko/docs/Web/JavaScript/Equality_comparisons_and_sameness))\n\n## 고급 예외처리(Advanced exception handling)\n\n`try...catch`와 `throw`로 고급 예외처리를 할 수 있습니다. 참고로 프로그램 실행 중에 발생하는 오류는 예외(Exception)이라고 하며, 코드의 문법적 오류는 에러(Error)라고 한다. `try...catch`는 구동 중 코드상에서 발생할 수 있는 오류들을 잡아준다.\n\n    try {\n      // try 블록은 예외가 발생할 수도 있는 부분\n    } catch(e) {\n      // catch 블록은 try 블록에서 예외가 발생했을 때 호출되는 부분\n      // 객체 e는 name과 message 속성을 가짐\n    } finally {\n      // 선택적으로 finally 블록을 추가할 수 있음\n      // 예외 발생 여부를 떠나 무조건 실행되어야 할 부분\n    }\n\n`throw`문은 강제로 예외를 발생시킬 때 사용합니다. `throw`의 문법은 다음과 같습니다:\n\n    throw expression;\n\n표현식의 값은 숫자, 문자 등 어떤 것이든 상관없습니다. `throw`가 발생하면 가장 가까운 `catch` 블록으로 이동합니다. 다음과 같이 사용하면 됩니다:\n\n    try {\n      if(a > 100) {\n        throw \"Value too high\";\n      }\n    } catch(e) {\n      alert(e);\n    }\n\n([참고1](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#예외처리문)) ([참고2](http://webclub.tistory.com/71))\n\n## Event delegation\n\n이벤트 리스너를 등록하는 것은 DOM 입장에서는 부담스러운 일이며, 리소스를 상당히 잡아먹는 작업입니다. 그래서 요소 하나하나에 이벤트를 등록하는 대신 위임을 하는 것이 바람직합니다. 자식 요소들을 감싼 부모 요소에 이벤트를 등록해 이벤트를 위임하면 각각의 자식 요소들에 이벤트를 등록하는 효과를 낼 수 있습니다. 이벤트를 위임하면 리소스를 절약할 수 있을 뿐만 아니라 DOM 트리에 새로운 요소를 추가했을 때 코드를 추가할 필요가 없어지고, 코드의 양을 줄일 수도 있습니다. 다음은 ul 요소의 자식 요소인 li 요소를 클릭하면 해당 li 요소가 사라지는 코드입니다.\n\n    <ul id=\"todoList\">\n      <li>TODO: A</li>\n      <li>TODO: B</li>\n      <li>TODO: C</li>\n    </ul>\n\n아주 평범한 리스트입니다.\n\n    function getTarget(e) {\n      if(!e) { // event 객체가 존재하지 않으면\n        e = window.event; // IE의 event 객체를 사용\n      }\n\n      return e.target || e.srcElement; // 이벤트가 발생한 요소를 가져옴\n    }\n\n    function itemDone(e) {\n      var elTarget, elParent;\n\n      elTarget = getTarget(e); // 이벤트가 발생한 요소 가져옴 (li)\n      elParent = target.parentNode; // 해당 요소의 부모 요소를 가져옴 (ul)\n      elParent.removeChild(elTarget); // 이벤트가 발생한 요소를 제거함 (li)\n    }\n\n    (function(){\n      var el = document.getElementById('todoList');\n\n      if(el.addEventListener) { // 이벤트 리스너가 지원되면\n        el.addEventListener('click', function(e) { // 클릭 이벤트에 리스너를 지정\n          itemDone(e);\n        }, false); // 이벤트 버블링을 사용\n      } else { // 이벤트 리스너가 지원되지 않으면\n        el.attachEvent('onclick', function(e) { // IE의 onclick 이벤트를 사용\n          itemDone(e);\n        }\n      });\n    })();\n\nIE까지 고려한 코드입니다. 하나의 이벤트 리스너로 요소 3개를 제어하고 있습니다다. jQuery는 보다 편하게 이벤트를 바인딩할 수 있도록 [`.delegate()`](http://api.jquery.com/delegate/) 메소드를 제공하고 있습니다.\n\n## Prototype 기반 상속\n\n자바스크립트는 클래스라는 개념이 없기때문에 프로토타입(Prototype)이 클래스를 대신해 객체지향 프로그래밍의 핵심을 맡습니다. (ES6부터 클래스 문법이 추가됐기 때문에 프로토타입에 직접 접근할 필요가 없어졌습니다. 그래도 프토로타입은 자바스크립트의 기반이기 때문에 알아두는 것이 좋습니다.) 자바스크립트에서는 이 프로토타입을 통해 다른 객체지향 언어에서 쓰이는 클래스를 구현할 수 있습니다.\n\n    function Player() {\n      this.hp = 100;\n      this.mp = 50;\n    }\n\n    var kim = new Player();\n    var park = new Player();\n\n    console.log(kim.hp); // 100\n    console.log(kim.mp); // 50\n\n    console.log(park.hp); // 100\n    console.log(park.mp); // 50\n\n`kim`과 `park`은 `hp`와 `mp`를 각각 100, 50씩 가지고 있습니다. 만약 객체를 100개 만들면 200개의 변수가 메모리에 할당됩니다. 프로토타입을 활용하면 메모리를 아낄 수 있습니다.\n\n    function Player() {}\n    Player.prototype.hp = 100;\n    Player.prototype.mp = 50;\n\n    var kim = new Player();\n    var park = new Player();\n\n`kim`과 `park`은 프로토타입에 연결된 `Player` 객체의 값을 가져다 쓸 수 있습니다. `hp`와 `mp`를 `kim`과 `park`이 공유하는 것입니다. 이러한 매커니즘으로 프로토타입을 이용해 상속을 구현할 수 있습니다. ([참고1](https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67)) ([참고2](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain))\n\n## null, undefined, undeclared의 차이\n\n이건 종종 헷갈리는 경우가 있습니다. null은 어떠한 값도 가리키지 않는다는 의미의 원시값입니다. 변수를 선언하고 null을 할당한 경우 해당 변수가 어떠한 값도 가지지 않았다는 의미가 됩니다. undefined는 변수가 선언됐지만 값이 할당되지 않았다는 것을 의미하는 값입니다. 즉, null은 개발자로부터 할당되는 값이고, undefined는 아예 할당을 하지 않은 상태입니다. undeclared는 변수 자체가 선언되지 않았다는 의미입니다. 콘솔에서는 undefined와 똑같이 표기되기 때문에 변수가 초기화되지 않았다는 것인지, 아예 선언되지 않았다는 것인지 확인할 필요가 있습니다.\n\n## 클로저(Closure)\n\n자바스크립트에서는 함수 안에 또 다른 함수를 선언할 수 있는데, 함수 안의 함수인 내부함수(inner function)는 외부함수(outer function)의 지역변수에 접근할 수 있습니다. 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수는 외부함수에 접근할 수 있습니다. 이러한 메커니즘을 클로저라고 합니다. MDN에 클로저를 활용한 재밌는 예제가 있습니다:\n\n    function makeAdder(x) {\n      return function(y) {\n        return x + y;\n      };\n    }\n\n    var add5 = makeAdder(5);\n    var add10 = makeAdder(10);\n\n    console.log(add5(2));  // 7\n    console.log(add10(2)); // 12\n\n여기서 `makeAdder(x)`는 `x`를 인자로 받아서 새로운 함수를 반환합니다. 그리고 반환되는 내부 함수는 `y`를 인자로 받아 `x + y`를 반환합니다. ([참고1](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures)) ([참고2](https://opentutorials.org/course/743/6544))\n\n## 자바스크립트 모듈 패턴(Module pattern)\n\n모듈 패턴(Module pattern)은 코드 설계 방법을 말합니다. 여기서는 객체를 public과 private으로 나누는 캡슐화가 핵심입니다. 자바스크립트는 public이나 private와 같은 접근 제한자를 제공하지 않지만, 클로저를 이용해 구현할 수 있습니다. private method는 코드 접근을 제한할 수 있을뿐만 아니라 추가적인 자바스크립트가 다른 스크립트와 이름이 충돌하는 것을 막을 수 있습니다. 매우 기본적인 방법은 모든 코드를 익명함수 안에 집어넣어 private 스코프로 만드는 것입니다. 하지만 이렇게 하면 코드를 재사용하기 불편해지기 때문에 별도의 네임스페이스를 적용해야 합니다. ([참고1](http://blog.javarouka.me/2012/02/javascripts-pattern-2-module-pattern.html)) ([참고2](http://asfirstalways.tistory.com/234)\n\n## 네이티브 객체(Native object), 호스트 객체(Host object), Built-in 객체\n\n객체는 크게 3가지로 구분됩니다. 네이티브 객체는 ECMAScript 명세에 정의된 객체로, 자바스크립트의 모든 엔진에 구현된 표준객체입니다. BOM(Browser Object Model)과 DOM 등은 모두 네이티브 객체며, 자바스크립트 엔진을 구동하는 측에서 빌드되는 객체입니다. 호스트 객체는 개발자가 정의한 객체입니다. 마지막으로 Built-in 객체는 자바스크립트 엔진을 구성하는 기본 객체들을 포함합니다. Number, String, Array, Date 등의 내장객체들이 있습니다. ([참고](https://github.com/hckoo/javascriptstudy2013/blob/master/1209/6%EC%9E%A5%20%EA%B0%9D%EC%B2%B4.md))\n\n## 기능 검출(Feature detection)과 기능 추론(Feature inference)\n\n기능 검출이란 스크립트가 호출하는 기능을 사용자의 브라우저가 지원하는지 체크하는 것을 말합니다. 다음은 브라우저가 GPS를 지원하는지 확인하는 코드입니다.\n\n    if(navigator.geolocation) {...}\n\n기능 추론도 기능 검출처럼 브라우저가 특정 기능을 지원하는지 체크하는 것입니다. 하지만 'A기능을 지원하면 B기능도 지원할 것이다.'라는 추론이 바탕이 됩니다. 별로 좋지 않은 방법입니다. ([참고](https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th))\n\n## 호이스팅(Hoisting)\n\n호이스팅은 인터프리터가 스크립트를 해석할 때, 변수의 정의가 스코프에 따라 선언과 할당으로 분리되어 선언을 항상 컨텍스트의 최상위로 끌어올리는 것을 의미합니다. 즉, 변수를 어디에서 선언하든 인터프리터는 최상단에서 선언한 것으로 해석합니다. 이는 변수 뿐 아니라 함수에도 적용됩니다. 따라서 상단에서 함수를 호출하고, 하단에서 함수를 정의해도 기능적인 문제는 없습니다. ([참고](https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th))\n\n## 이벤트 흐름(Event flow)\n\nHTML 요소가 다른 요소의 내부에 중첩되어 있을 때 자식 요소를 클릭하면 부모 요소를 클릭한 셈이 됩니다. 이처럼 이벤트는 흐름을 가지고 있으며, 이것을 이벤트 흐름이라고 부릅니다. 이벤트 흐름에는 두 가지 방식이 있습니다. 먼저 이벤트 버블링(Event bubbling)은 이벤트가 직접적으로 발생한 노드로부터 시작해 바깥 노드로 이벤트가 퍼져 나가는 방식을 말합니다. 대부분의 브라우저가 기본적으로 이 방식을 지원합니다. 반대로 이벤트 캡쳐링(Event capturing)은 바깥 노드부터 시작해서 안쪽으로 퍼지는 방식입니다. IE8 혹은 그 이전 버전에서는 지원되지 않습니다.\n\n## document load event와 DOMContentLoaded event\n\nDOM을 제어하는 스크립트는 마크업의 모든 요소에 대한 처리가 끝난 뒤에 로드되어야 합니다. 그래서 보통 body 태그 최하단에서 스크립트를 불러오도록합니다. 또 다른 방법은 이벤트를 이용하는 것입니다. document load event는 페이지의 모든 리소스가 로드된 이후에 실행됩니다. 때문에 구동이 지연되어 사용자 경험을 저하할 수 있습니다. 반면 DOMContentLoaded event는 스크립트 로드를 마치고 실행이 가능한 시점에 바로 실행됩니다. ([참고](https://opentutorials.org/module/904/6765))\n\n## 조건부 삼항 연산자(Conditional ternary operator)\n\n보통 그냥 '삼항 연산자'이라고 부릅니다. if문을 축약해서 쓸 수 있는 유용한 연산자이지만, 과하게 사용하면 코드의 가독성을 떨어뜨릴 수 있습니다.\n\n    var a = 1, b = 2;\n\n    console.log(a < b ? \"True\" : \"False\"); // logs \"True\"\n    console.log(a > b ? \"True\" : \"False\"); // logs \"False\"\n\n    a < b ? (\n      console.log(\"True\");\n      alert(\"True\");\n    ) : (\n      console.log(\"False\");\n      alert(\"False\");\n    ); // logs \"True\"\n\n조건이 true면 전자의 값을 반환하고, false면 후자의 값을 반환합니다.\n\n## use strict\n\nES6에서 새로 추가된 기능으로, 스크립트에 `\"use strict;\"` 구문을 추가하면 strict mode에서 실행하게 됩니다. strict mode는 코딩 실수를 찾아 예외를 발생시키고, 전역 객체에 접근하는 것과 같은 위험한 액션을 막습니다. 스크립트 전체에 적용할 수도 있고 특정 함수에만 적용할 수도 있습니다. ([참고](https://stackoverflow.com/questions/1335851/what-does-use-strict-do-in-javascript-and-what-is-the-reasoning-behind-it))\n\n## Call stack과 Task queue\n\n자바스크립트 엔진은 요청이 들어올 때마다 요청을 순차적으로 call stack에 담아 하나씩 처리합니다. call stack은 하나만 존재하기 때문에 요청도 하나씩만 처리할 수 밖에 없습니다. task queue는 처리해야 하는 task를 임시로 저장해두는 큐입니다. call stack이 비워지면 task queue에 있던 task들이 순서대로 call stack에 push됩니다. 가령 `setTimeout()` 함수로 10초의 딜레이를 둔다면, 그동안 `setTimeout()`이 처리할 task는 task queue에 쌓이고 다른 부분의 스크립트들이 실행됩니다. 따라서 딜레이를 0으로 줘도 `setTimeout()`의 task는 다른 것들보다 나중에 처리됩니다. ([참고](https://github.com/nhnent/fe.javascript/wiki/June-13-June-17,-2016))",
      "json_metadata": "{\"tags\":[\"front-end\",\"development\",\"web\",\"kr\",\"dfdfdf\"],\"users\":[\"import\"],\"image\":[\"https://cloud.githubusercontent.com/assets/11814588/9597163/cb8fdf6a-50b9-11e5-80f0-da960d3f88cc.gif\"],\"links\":[\"https://github.com/h5bp/Front-end-Developer-Interview-Questions\",\"https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation\",\"https://stackoverflow.com/questions/3684923/javascript-variables-declare-outside-or-inside-loop\",\"http://www.clearboth.org/51_graceful_degradation_versus_progressive_enhancement/\",\"http://webdir.tistory.com/416\",\"https://developer.mozilla.org/ko/docs/Web/Accessibility/ARIA\",\"http://www.bloter.net/archives/148889\",\"https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy\",\"https://brunch.co.kr/@99-life/2\",\"http://www.internetmap.kr/entry/Crossorigin-resource-sharing\",\"http://webdir.tistory.com/40\",\"https://developer.mozilla.org/ko/docs/Web/HTML/Quirks_Mode_and_Standards_Mode\",\"http://blog.wystan.net/2007/05/24/xhtml-vs-html\",\"https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/data-*\",\"https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies\",\"https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage\",\"https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage\",\"https://appletree.or.kr/blog/web-development/javascript/script-%ED%83%9C%EA%B7%B8%EC%9D%98-async%EC%99%80-defer-%EC%86%8D%EC%84%B1/\",\"https://stackoverflow.com/questions/33651166/what-is-progressive-rendering\",\"https://meyerweb.com/eric/tools/css/reset/\",\"https://necolas.github.io/normalize.css/\",\"https://brunch.co.kr/@techhtml/21\",\"http://naradesign.net/wp/2008/05/27/144/\",\"https://www.sitepoint.com/css-image-replacement-text-indent-negative-margins-and-more/\",\"https://github.com/nhnent/fe.javascript/wiki/%EB%B0%95%EC%8A%A4%EB%AA%A8%EB%8D%B8\",\"http://bootstrapk.com/css/#grid\",\"https://studio-jt.co.kr/%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A0%81%EC%9D%91%ED%98%95-%EC%9B%B9/\",\"https://developer.mozilla.org/ko/docs/Web/JavaScript/Equality_comparisons_and_sameness\",\"https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#예외처리문\",\"http://webclub.tistory.com/71\",\"http://api.jquery.com/delegate/\",\"https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67\",\"https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain\",\"https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures\",\"https://opentutorials.org/course/743/6544\",\"http://blog.javarouka.me/2012/02/javascripts-pattern-2-module-pattern.html\",\"http://asfirstalways.tistory.com/234\",\"https://github.com/hckoo/javascriptstudy2013/blob/master/1209/6%EC%9E%A5%20%EA%B0%9D%EC%B2%B4.md\",\"https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th\",\"https://opentutorials.org/module/904/6765\",\"https://stackoverflow.com/questions/1335851/what-does-use-strict-do-in-javascript-and-what-is-the-reasoning-behind-it\",\"https://github.com/nhnent/fe.javascript/wiki/June-13-June-17,-2016\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "front-end",
      "permlink": "4dkun",
      "title": "프론트엔드 개발자를 위한 토막상식"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:26:30",
  "trx_id": "d0440e04030eb07d5edb41774d93e7c64d026c1a",
  "trx_in_block": 48,
  "virtual_op": 0
}
caesium133published a new post: 63ddrw
2018/02/13 03:26:09
authorcaesium133
body고양이가 글을 씁니다! ![pixel cat](https://i.imgur.com/PyMw8Cb.png)
json metadata{"tags":["cat","kr"],"image":["https://i.imgur.com/PyMw8Cb.png"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkcat
permlink63ddrw
title고양이가 쓴 글
Transaction InfoBlock #19822713/Trx c89efb9df667166d234fd9ffa4780122db695e8c
View Raw JSON Data
{
  "block": 19822713,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "고양이가 글을 씁니다!\n\n![pixel cat](https://i.imgur.com/PyMw8Cb.png)",
      "json_metadata": "{\"tags\":[\"cat\",\"kr\"],\"image\":[\"https://i.imgur.com/PyMw8Cb.png\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "cat",
      "permlink": "63ddrw",
      "title": "고양이가 쓴 글"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:26:09",
  "trx_id": "c89efb9df667166d234fd9ffa4780122db695e8c",
  "trx_in_block": 34,
  "virtual_op": 0
}
2018/02/13 03:25:48
authorcaesium133
body오 좋은 팁 감사합니다!
json metadata{"tags":["cat"],"app":"steemit/0.1"}
parent authordrgkim
parent permlinkre-caesium133-63ddrw-20180212t022249827z
permlinkre-drgkim-re-caesium133-63ddrw-20180213t032547263z
title
Transaction InfoBlock #19822706/Trx bbef1f988e239341efa8476f0059f10d30b1bbaf
View Raw JSON Data
{
  "block": 19822706,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "오 좋은 팁 감사합니다!",
      "json_metadata": "{\"tags\":[\"cat\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "drgkim",
      "parent_permlink": "re-caesium133-63ddrw-20180212t022249827z",
      "permlink": "re-drgkim-re-caesium133-63ddrw-20180213t032547263z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T03:25:48",
  "trx_id": "bbef1f988e239341efa8476f0059f10d30b1bbaf",
  "trx_in_block": 47,
  "virtual_op": 0
}
2018/02/13 02:53:06
idfollow
json["follow",{"follower":"caesium133","following":"testament","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19822053/Trx fe49bcd163f7774b33cbdc54fce8d144bcd913b7
View Raw JSON Data
{
  "block": 19822053,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"testament\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T02:53:06",
  "trx_id": "fe49bcd163f7774b33cbdc54fce8d144bcd913b7",
  "trx_in_block": 4,
  "virtual_op": 0
}
caesium133published a new post: 21-1
2018/02/13 02:52:18
authorcaesium133
body![Modern software development](https://i.imgur.com/KzCHMAx.gif) 솔직히 프론트엔드 개발에 대해 이 움짤보다 더 잘 설명할 자신이 없습니다. 당장 [2018년 웹 개발자 로드맵](https://github.com/kamranahmedse/developer-roadmap)만 봐도 배울게 넘칩니다. (그리고 지금 이 시간에도 계속해서 늘어나고 있습니다.) 이제 막 프론트엔드 개발을 시작하려는 분이나 오래전에 웹 개발을 배운 뒤 정보를 업데이트하지 않은 분들은 아득한 기분이 들겁니다. 그래도 막상 시작해보면 생각만큼 어렵지는 않습니다. 모든 것이 서로 얽혀있기 때문에 하나를 배우면 동시에 다른 하나를 같이 배우게 됩니다. 이번 글에서만 npm과 npm scripts, webpack을 다룹니다. 너무 걱정하지 마세요. # 오래된 방법 처음 웹 프로그래밍을 배울 때는 HTML 파일에 외부 CSS나 자바스크립트 코드를 `<link>`태그나 `<script>`태그로 가져오라고 배웁니다. 이렇게요: <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="index.js"></script> </body> </html> 이정도만 해도 웹사이트는 잘 돌아갑니다. 하지만 많은 점이 불편합니다. * 만약 자바스크립트 코드가 방대해져서 파일을 30개로 쪼갠다면 `index.html`에는 30개의 script태그가 필요하게 됩니다. CSS도 마찬가지구요. * 뿐만 아니라 외부 자바스크립트 라이브러리를 가져올 때도 문제가 생깁니다. 만일 jQuery 3.3.1 버전을 다운받아서 `<script src="jquery-3.3.1.min.js"></script>`와 같은 코드를 작성한다면 jQuery 3.4.0 버전이 나왔을 땐 다시 코드를 다운받는 수고를 해야합니다. * 다른 사람과 프로젝트를 공유할 때도 불편합니다. 어떤 파일이 어떤 라이브러리나 프레임워크를 사용하고 있는지 일일히 설명해야하고, 또 그 파일들을 모두 공유해야 하니까요. # npm(node package manager) 사용하기 그래서 npm이라고 하는 '패키지 매니저'를 사용합니다. (원래는 대부분 [Bower](https://bower.io/)를 사용했는데, 이젠 npm이 더 인기있습니다.) npm은 마치 앱스토어에서 어플리케이션을 다운받아 쓰듯이 자바스크립트 패키지를 쉽게 찾고 다운받아서 웹사이트에 적용시킬 수 있도록 만들어줍니다. 이렇게 하면 해당 웹사이트가 어떤 패키지를 사용하고 있는지 한 눈에 볼 수 있고, 패키지의 버전 관리도 훨씬 쉬워집니다. npm은 Node.js를 기반으로 합니다. [Node.js 홈페이지](https://nodejs.org/ko/)에서 안정적인 LTS버전을 다운받으세요. Node.js를 설치하면 npm도 설치됩니다. npm을 사용한다는 것은 CLI 환경에서 작업을 한다는 것을 의미합니다. 많은 분들이 커맨드 라인을 보면 겁을 먹는데, 너무 걱정하지 않으셔도 됩니다. 익숙해지면 오히려 GUI보다 편합니다. 자, 설치가 끝났다면 이제 터미널이나 cmd를 열고 잘 설치되었는지 확인해보세요. Node.js 버전 확인: $ node -v npm 버전 확인: $ npm -v 이제 21세기 프론트엔드 개발 환경 세팅에 첫 삽을 떴습니다. `index.html`이 있는 작업 디렉토리로 이동해서 npm 환경을 초기화해줍니다. $ npm init 이것저것 설정하는 질문이 뜰텐데 그냥 모두 엔터를 눌러주세요. 나중에 수정할 수 있습니다. 설정이 끝났다면 `index.html`이 있는 디렉토리에 `package.json`이라는 새로운 파일이 생깁니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } 이제 npm으로 jQuery를 설치해볼까요? $ npm install jquery 이 명령어를 실행하면 npm의 중앙 저장소에서 jQuery를 다운받아 `node_modules`라는 폴더에 저장합니다. `package.json`을 새로고침하면 내용이 바뀐 것을 볼 수 있습니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "jquery": "^3.3.1" } } 정말 편합니다. 이제 다른 사람과 프로젝트를 함께 할 때 `node_modules` 폴더를 주지 않고 `package.json` 파일을 주면 상대방은 `$ npm install` 명령어를 통해 필요한 자바스크립트 패키지를 한번에 다운받을 수 있게 됩니다. `node_modules/jquery/dist/`폴더를 보면 jQuery 코드들이 모여있는 것을 볼 수 있습니다. <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="node_modules/jquery/dist/jquery.min.js"></script> <script src="index.js"></script> </body> </html> 이렇게 하면 `index.js`에서 jQuery를 사용할 수 있습니다. 하지만 패키지를 설치하고 불러 올때마다 `node_moduels` 폴더를 뒤져봐야 하는 것은 여전히 불편합니다. 하지만 이것도 쉽게 해결할 수 있습니다. // index.js var $ = require('jquery'); console.log($('#title').text()); `jquery`는 전역에서 사용 가능하며, Node.js는 서버사이드 언어이기 때문에 패키지의 경로를 따로 지정해주지 않아도 모두 알아서 패키지를 가져와 줍니다. `index.html`에서 jQuery를 불러오는 `<script>`태그를 지우고 사이트를 새로고침해보세요. 어떤가요? 아쉽지만 브라우저는 `require`를 모르기 때문에 에러가 뜹니다. (브라우저는 파일 시스템에 접근할 권한을 가지고 있지 않습니다.) 그래서 우리는 webpack를 사용합니다. # webpack 사용하기 webpack은 '모듈 번들러'입니다. [Browserify](http://browserify.org/)라는 모듈 번들러도 있는데, webpack은 번들링 뿐만 아니라 부가적인 기능을 많이 제공하고 있어 매우 편리합니다. 번들링이라는 것은 여러 자바스크립트 파일들을 하나로 합친다는 의미입니다. 현재 `index.html`에서는 `jquery.min.js`와 `index.js`를 따로 불러들이고 있는데, webpack을 사용하면 `bundle.js`와 같은 파일 하나만 부를 수 있게 됩니다. 또한 앞서 사용하지 못한 `require`도 사용할 수 있게 됩니다. 그럼 webpack을 설치해봅니다. $ npm install webpack --save-dev `--save-dev`은 개발 의존으로 설치하겠다는 의미입니다. 즉, webpack이 개발용으로만 쓰이고 실제 배포판에서는 사용되지 않을 것이라는 뜻입니다. `package.json`을 확인해보면 아까 설치한 jquery와는 다르게 `devDependencies`로 설치된 것을 볼 수 있습니다. 그럼 이제 webpack을 세팅해볼까요? webpack을 세팅하기 위해서는 `webpack.config.js`라는 파일을 만들어야 합니다. 파일 이름은 꼭 `webpack.config.js`이어야 webpack이 실행될 때 이 파일을 참조하게 됩니다. // webpack.config.js module.exports = { entry: './index.js', output: { filename: 'bundle.js' } }; `entry`는 번들링할 파일, `output`은 번들링한 결과물로 나올 파일을 의미합니다. 우리는 `index.js`를 번들링해서 `bundle.js`로 내보낼 것입니다. `index.html`을 수정합시다. <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1>Hello, world!</h1> <script src="bundle.js"></script> </body> </html> 그리고 webpack을 실행하면! $ ./node_modules/.bin/webpack `bundle.js`가 만들어지고, 사이트도 잘 돌아갑니다. 하지만 매번 `index.js`를 수정할 때마다 webpack을 실행해주는 건 불편합니다. 이건 어떡할까요? # task runner 사용하기 task runner는 빌드를 자동화해주는 도구입니다. 예전에는 [Grunt](https://gruntjs.com/)나 [Gulp](https://gulpjs.com/)가 쓰였지만, 요즘은 npm에 포함되어 있는 스크립트 기능을 사용하는 추세입니다. 별도의 패키지를 설치할 필요 없이 `package.json`만 수정하면 바로 되니까 정말 편합니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", }, "dependencies": { "jquery": "^3.3.1" } } `build`와 `watch`라는 명령어를 추가했습니다. $ npm run build `build`를 하면 webpack이 실행되며 코드를 빌드해줍니다. UglifyJS와 같은 플러그인을 설치하고 스크립트를 조금 수정하면 minify된 배포용 코드를 만들 수도 있습니다. 이건 다음 글에서 다루겠습니다. $ npm run watch `watch`는 자바스크립트 파일이 수정될 때마다 자동으로 webpack을 실행시켜줍니다. 한 번만 켜두면 자바스크립트 코드를 수정하고 저장할 때마다 webpack이 실행되어 모든 코드가 번들링됩니다. 이제 여기에 웹 서버를 돌리면 개발환경을 더 편리하게 만들 수 있습니다. # 웹 서버 사용하기 아파치나 Nginx를 설치하지 않아도 웹서버를 구축할 수 있습니다. webpack이 제공하는 웹서버를 설치하면 됩니다. $ npm install webpack-dev-server --save-dev 설치가 끝나면 `package.json`에 스크립트를 추가해줍니다. { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", "webpack-dev-server": "^2.11.1" }, "dependencies": { "jquery": "^3.3.1" } } `build`와 `watch`처럼 `server`를 사용할 수 있게 되었습니다. 그리고 `webpack.config.js`에도 서버에 대한 설정을 추가해줍니다. module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 8000 }, }; 그리고 서버를 실행하세요. $ npm run server 이렇게 하면 `webpack.config.js`가 있는 디렉토리의 `index.html`이 `localhost:8000`에서 열립니다. 자바스크립트 파일을 수정하면 브라우저를 새로고침해주기도 합니다. 정말 엄청나게 편해졌죠. 이제 기본적인 세팅을 모두 끝냈습니다. 이번 글에서 우리는 5개의 파일(`index.html`, `index.js`, `bundle.js`, `package.json`, `webpack.config.js`)과 하나의 폴더(`node_modules`)를 얻었습니다. **index.html** <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>index</title> </head> <body> <h1 id="title">Hello, world!</h1> <script src="bundle.js"></script> </body> </html> **index.js** var $ = require('jquery'); console.log($('#title').text()); **package.json** { "name": "project-name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --progress -p", "watch": "webpack --progress --watch", "server": "webpack-dev-server --open" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.10.0", "webpack-dev-server": "^2.11.1" }, "dependencies": { "jquery": "^3.3.1" } } **webpack.config.js** module.exports = { entry: './index.js', output: { filename: 'bundle.js' }, devServer: { contentBase: './', compress: true, port: 8000 }, };
json metadata{"tags":["font-end","javascript","webpack","npm"],"image":["https://i.imgur.com/KzCHMAx.gif"],"links":["https://github.com/kamranahmedse/developer-roadmap","https://bower.io/","https://nodejs.org/ko/","http://browserify.org/","https://gruntjs.com/","https://gulpjs.com/"],"app":"steemit/0.1","format":"markdown"}
parent author
parent permlinkfont-end
permlink21-1
title21세기 프론트엔드 (1) 시작하기
Transaction InfoBlock #19822037/Trx 3b1a3ee0a9866df33d7963155cf35150245abb07
View Raw JSON Data
{
  "block": 19822037,
  "op": [
    "comment",
    {
      "author": "caesium133",
      "body": "![Modern software development](https://i.imgur.com/KzCHMAx.gif)\n\n솔직히 프론트엔드 개발에 대해 이 움짤보다 더 잘 설명할 자신이 없습니다. 당장 [2018년 웹 개발자 로드맵](https://github.com/kamranahmedse/developer-roadmap)만 봐도 배울게 넘칩니다. (그리고 지금 이 시간에도 계속해서 늘어나고 있습니다.) 이제 막 프론트엔드 개발을 시작하려는 분이나 오래전에 웹 개발을 배운 뒤 정보를 업데이트하지 않은 분들은 아득한 기분이 들겁니다. 그래도 막상 시작해보면 생각만큼 어렵지는 않습니다. 모든 것이 서로 얽혀있기 때문에 하나를 배우면 동시에 다른 하나를 같이 배우게 됩니다. 이번 글에서만 npm과 npm scripts, webpack을 다룹니다. 너무 걱정하지 마세요.\n\n# 오래된 방법\n\n처음 웹 프로그래밍을 배울 때는 HTML 파일에 외부 CSS나 자바스크립트 코드를 `<link>`태그나 `<script>`태그로 가져오라고 배웁니다. 이렇게요:\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"index.js\"></script>\n    </body>\n    </html>\n\n이정도만 해도 웹사이트는 잘 돌아갑니다. 하지만 많은 점이 불편합니다.\n\n*   만약 자바스크립트 코드가 방대해져서 파일을 30개로 쪼갠다면 `index.html`에는 30개의 script태그가 필요하게 됩니다. CSS도 마찬가지구요.\n*   뿐만 아니라 외부 자바스크립트 라이브러리를 가져올 때도 문제가 생깁니다. 만일 jQuery 3.3.1 버전을 다운받아서 `<script src=\"jquery-3.3.1.min.js\"></script>`와 같은 코드를 작성한다면 jQuery 3.4.0 버전이 나왔을 땐 다시 코드를 다운받는 수고를 해야합니다.\n*   다른 사람과 프로젝트를 공유할 때도 불편합니다. 어떤 파일이 어떤 라이브러리나 프레임워크를 사용하고 있는지 일일히 설명해야하고, 또 그 파일들을 모두 공유해야 하니까요.\n\n# npm(node package manager) 사용하기\n\n그래서 npm이라고 하는 '패키지 매니저'를 사용합니다. (원래는 대부분 [Bower](https://bower.io/)를 사용했는데, 이젠 npm이 더 인기있습니다.) npm은 마치 앱스토어에서 어플리케이션을 다운받아 쓰듯이 자바스크립트 패키지를 쉽게 찾고 다운받아서 웹사이트에 적용시킬 수 있도록 만들어줍니다. 이렇게 하면 해당 웹사이트가 어떤 패키지를 사용하고 있는지 한 눈에 볼 수 있고, 패키지의 버전 관리도 훨씬 쉬워집니다.\n\nnpm은 Node.js를 기반으로 합니다. [Node.js 홈페이지](https://nodejs.org/ko/)에서 안정적인 LTS버전을 다운받으세요. Node.js를 설치하면 npm도 설치됩니다. npm을 사용한다는 것은 CLI 환경에서 작업을 한다는 것을 의미합니다. 많은 분들이 커맨드 라인을 보면 겁을 먹는데, 너무 걱정하지 않으셔도 됩니다. 익숙해지면 오히려 GUI보다 편합니다. 자, 설치가 끝났다면 이제 터미널이나 cmd를 열고 잘 설치되었는지 확인해보세요.\n\nNode.js 버전 확인:\n\n    $ node -v\n\nnpm 버전 확인:\n\n    $ npm -v\n\n이제 21세기 프론트엔드 개발 환경 세팅에 첫 삽을 떴습니다. `index.html`이 있는 작업 디렉토리로 이동해서 npm 환경을 초기화해줍니다.\n\n    $ npm init\n\n이것저것 설정하는 질문이 뜰텐데 그냥 모두 엔터를 눌러주세요. 나중에 수정할 수 있습니다. 설정이 끝났다면 `index.html`이 있는 디렉토리에 `package.json`이라는 새로운 파일이 생깁니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\"\n    }\n\n이제 npm으로 jQuery를 설치해볼까요?\n\n    $ npm install jquery\n\n이 명령어를 실행하면 npm의 중앙 저장소에서 jQuery를 다운받아 `node_modules`라는 폴더에 저장합니다. `package.json`을 새로고침하면 내용이 바뀐 것을 볼 수 있습니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n정말 편합니다. 이제 다른 사람과 프로젝트를 함께 할 때 `node_modules` 폴더를 주지 않고 `package.json` 파일을 주면 상대방은 `$ npm install` 명령어를 통해 필요한 자바스크립트 패키지를 한번에 다운받을 수 있게 됩니다. `node_modules/jquery/dist/`폴더를 보면 jQuery 코드들이 모여있는 것을 볼 수 있습니다.\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"node_modules/jquery/dist/jquery.min.js\"></script>\n      <script src=\"index.js\"></script>\n    </body>\n    </html>\n\n이렇게 하면 `index.js`에서 jQuery를 사용할 수 있습니다. 하지만 패키지를 설치하고 불러 올때마다 `node_moduels` 폴더를 뒤져봐야 하는 것은 여전히 불편합니다. 하지만 이것도 쉽게 해결할 수 있습니다.\n\n    // index.js\n    var $ = require('jquery');\n    console.log($('#title').text());\n\n`jquery`는 전역에서 사용 가능하며, Node.js는 서버사이드 언어이기 때문에 패키지의 경로를 따로 지정해주지 않아도 모두 알아서 패키지를 가져와 줍니다. `index.html`에서 jQuery를 불러오는 `<script>`태그를 지우고 사이트를 새로고침해보세요. 어떤가요? 아쉽지만 브라우저는 `require`를 모르기 때문에 에러가 뜹니다. (브라우저는 파일 시스템에 접근할 권한을 가지고 있지 않습니다.) 그래서 우리는 webpack를 사용합니다.\n\n# webpack 사용하기\n\nwebpack은 '모듈 번들러'입니다. [Browserify](http://browserify.org/)라는 모듈 번들러도 있는데, webpack은 번들링 뿐만 아니라 부가적인 기능을 많이 제공하고 있어 매우 편리합니다. 번들링이라는 것은 여러 자바스크립트 파일들을 하나로 합친다는 의미입니다. 현재 `index.html`에서는 `jquery.min.js`와 `index.js`를 따로 불러들이고 있는데, webpack을 사용하면 `bundle.js`와 같은 파일 하나만 부를 수 있게 됩니다. 또한 앞서 사용하지 못한 `require`도 사용할 수 있게 됩니다. 그럼 webpack을 설치해봅니다.\n\n    $ npm install webpack --save-dev\n\n`--save-dev`은 개발 의존으로 설치하겠다는 의미입니다. 즉, webpack이 개발용으로만 쓰이고 실제 배포판에서는 사용되지 않을 것이라는 뜻입니다. `package.json`을 확인해보면 아까 설치한 jquery와는 다르게 `devDependencies`로 설치된 것을 볼 수 있습니다.\n\n그럼 이제 webpack을 세팅해볼까요? webpack을 세팅하기 위해서는 `webpack.config.js`라는 파일을 만들어야 합니다. 파일 이름은 꼭 `webpack.config.js`이어야 webpack이 실행될 때 이 파일을 참조하게 됩니다.\n\n    // webpack.config.js\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      }\n    };\n\n`entry`는 번들링할 파일, `output`은 번들링한 결과물로 나올 파일을 의미합니다. 우리는 `index.js`를 번들링해서 `bundle.js`로 내보낼 것입니다. `index.html`을 수정합시다.\n\n    <!-- index.html -->\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1>Hello, world!</h1>\n      <script src=\"bundle.js\"></script>\n    </body>\n    </html>\n\n그리고 webpack을 실행하면!\n\n    $ ./node_modules/.bin/webpack\n\n`bundle.js`가 만들어지고, 사이트도 잘 돌아갑니다. 하지만 매번 `index.js`를 수정할 때마다 webpack을 실행해주는 건 불편합니다. 이건 어떡할까요?\n\n# task runner 사용하기\n\ntask runner는 빌드를 자동화해주는 도구입니다. 예전에는 [Grunt](https://gruntjs.com/)나 [Gulp](https://gulpjs.com/)가 쓰였지만, 요즘은 npm에 포함되어 있는 스크립트 기능을 사용하는 추세입니다. 별도의 패키지를 설치할 필요 없이 `package.json`만 수정하면 바로 되니까 정말 편합니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n`build`와 `watch`라는 명령어를 추가했습니다.\n\n    $ npm run build\n\n`build`를 하면 webpack이 실행되며 코드를 빌드해줍니다. UglifyJS와 같은 플러그인을 설치하고 스크립트를 조금 수정하면 minify된 배포용 코드를 만들 수도 있습니다. 이건 다음 글에서 다루겠습니다.\n\n    $ npm run watch\n\n`watch`는 자바스크립트 파일이 수정될 때마다 자동으로 webpack을 실행시켜줍니다. 한 번만 켜두면 자바스크립트 코드를 수정하고 저장할 때마다 webpack이 실행되어 모든 코드가 번들링됩니다. 이제 여기에 웹 서버를 돌리면 개발환경을 더 편리하게 만들 수 있습니다.\n\n# 웹 서버 사용하기\n\n아파치나 Nginx를 설치하지 않아도 웹서버를 구축할 수 있습니다. webpack이 제공하는 웹서버를 설치하면 됩니다.\n\n    $ npm install webpack-dev-server --save-dev\n\n설치가 끝나면 `package.json`에 스크립트를 추가해줍니다.\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\",\n        \"server\": \"webpack-dev-server --open\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n        \"webpack-dev-server\": \"^2.11.1\"\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n`build`와 `watch`처럼 `server`를 사용할 수 있게 되었습니다. 그리고 `webpack.config.js`에도 서버에 대한 설정을 추가해줍니다.\n\n        module.exports = {\n          entry: './index.js',\n          output: {\n            filename: 'bundle.js'\n          },\n          devServer: {\n            contentBase: './',\n            compress: true,\n            port: 8000\n          },\n        };\n\n그리고 서버를 실행하세요.\n\n    $ npm run server\n\n이렇게 하면 `webpack.config.js`가 있는 디렉토리의 `index.html`이 `localhost:8000`에서 열립니다. 자바스크립트 파일을 수정하면 브라우저를 새로고침해주기도 합니다. 정말 엄청나게 편해졌죠. 이제 기본적인 세팅을 모두 끝냈습니다. 이번 글에서 우리는 5개의 파일(`index.html`, `index.js`, `bundle.js`, `package.json`, `webpack.config.js`)과 하나의 폴더(`node_modules`)를 얻었습니다.\n\n**index.html**\n\n    <!DOCTYPE html>\n    <html>\n    <head>\n      <meta charset=\"UTF-8\" />\n      <title>index</title>\n    </head>\n    <body>\n      <h1 id=\"title\">Hello, world!</h1>\n      <script src=\"bundle.js\"></script>\n    </body>\n    </html>\n\n**index.js**\n\n    var $ = require('jquery');\n    console.log($('#title').text());\n\n**package.json**\n\n    {\n      \"name\": \"project-name\",\n      \"version\": \"1.0.0\",\n      \"description\": \"\",\n      \"main\": \"index.js\",\n      \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n        \"build\": \"webpack --progress -p\",\n        \"watch\": \"webpack --progress --watch\",\n        \"server\": \"webpack-dev-server --open\"\n      },\n      \"author\": \"\",\n      \"license\": \"ISC\",\n      \"devDependencies\": {\n        \"webpack\": \"^3.10.0\",\n        \"webpack-dev-server\": \"^2.11.1\"\n      },\n      \"dependencies\": {\n        \"jquery\": \"^3.3.1\"\n      }\n    }\n\n**webpack.config.js**\n\n    module.exports = {\n      entry: './index.js',\n      output: {\n        filename: 'bundle.js'\n      },\n      devServer: {\n        contentBase: './',\n        compress: true,\n        port: 8000\n      },\n    };",
      "json_metadata": "{\"tags\":[\"font-end\",\"javascript\",\"webpack\",\"npm\"],\"image\":[\"https://i.imgur.com/KzCHMAx.gif\"],\"links\":[\"https://github.com/kamranahmedse/developer-roadmap\",\"https://bower.io/\",\"https://nodejs.org/ko/\",\"http://browserify.org/\",\"https://gruntjs.com/\",\"https://gulpjs.com/\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
      "parent_author": "",
      "parent_permlink": "font-end",
      "permlink": "21-1",
      "title": "21세기 프론트엔드 (1) 시작하기"
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-13T02:52:18",
  "trx_id": "3b1a3ee0a9866df33d7963155cf35150245abb07",
  "trx_in_block": 21,
  "virtual_op": 0
}
drgkimupvoted (100.00%) @caesium133 / 63ddrw
2018/02/12 02:22:54
authorcaesium133
permlink63ddrw
voterdrgkim
weight10000 (100.00%)
Transaction InfoBlock #19792668/Trx 965030b2e554d1c41ea3666030f1b5333fb66f3c
View Raw JSON Data
{
  "block": 19792668,
  "op": [
    "vote",
    {
      "author": "caesium133",
      "permlink": "63ddrw",
      "voter": "drgkim",
      "weight": 10000
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-12T02:22:54",
  "trx_id": "965030b2e554d1c41ea3666030f1b5333fb66f3c",
  "trx_in_block": 19,
  "virtual_op": 0
}
2018/02/12 02:22:45
authordrgkim
body안녕하세요? 귀여운 도트 고양이네요 ㅎㅎ 태그에 kr을 붙이시면 한국분들이 더 많이 글을 보실 수 있을것 같아요!
json metadata{"tags":["cat"],"app":"steemit/0.1"}
parent authorcaesium133
parent permlink63ddrw
permlinkre-caesium133-63ddrw-20180212t022249827z
title
Transaction InfoBlock #19792665/Trx 66dc1573549bce71df8279bab89834766fafc0cd
View Raw JSON Data
{
  "block": 19792665,
  "op": [
    "comment",
    {
      "author": "drgkim",
      "body": "안녕하세요? 귀여운 도트 고양이네요 ㅎㅎ\n\n태그에 kr을 붙이시면 한국분들이 더 많이 글을 보실 수 있을것 같아요!",
      "json_metadata": "{\"tags\":[\"cat\"],\"app\":\"steemit/0.1\"}",
      "parent_author": "caesium133",
      "parent_permlink": "63ddrw",
      "permlink": "re-caesium133-63ddrw-20180212t022249827z",
      "title": ""
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-12T02:22:45",
  "trx_id": "66dc1573549bce71df8279bab89834766fafc0cd",
  "trx_in_block": 54,
  "virtual_op": 0
}
2018/02/11 12:02:03
idfollow
json["follow",{"follower":"caesium133","following":"coffeenut","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775470/Trx e837cef4496c600488206d65cb73bcfdc0365b4f
View Raw JSON Data
{
  "block": 19775470,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"coffeenut\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:03",
  "trx_id": "e837cef4496c600488206d65cb73bcfdc0365b4f",
  "trx_in_block": 53,
  "virtual_op": 0
}
2018/02/11 12:02:03
idfollow
json["follow",{"follower":"caesium133","following":"coinkorea","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775470/Trx 03559426d4573de05fdbc54050a779046496c576
View Raw JSON Data
{
  "block": 19775470,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"coinkorea\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:03",
  "trx_id": "03559426d4573de05fdbc54050a779046496c576",
  "trx_in_block": 43,
  "virtual_op": 0
}
2018/02/11 12:02:03
idfollow
json["follow",{"follower":"caesium133","following":"coinyawong","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775470/Trx 1a446327284087fc84c80034f6c027142d21c915
View Raw JSON Data
{
  "block": 19775470,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"coinyawong\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:03",
  "trx_id": "1a446327284087fc84c80034f6c027142d21c915",
  "trx_in_block": 8,
  "virtual_op": 0
}
2018/02/11 12:02:00
idfollow
json["follow",{"follower":"caesium133","following":"cookingpapa","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775469/Trx f9330dc36c63a095ac1d3cfa0596251bdc7bf0cd
View Raw JSON Data
{
  "block": 19775469,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"cookingpapa\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:00",
  "trx_id": "f9330dc36c63a095ac1d3cfa0596251bdc7bf0cd",
  "trx_in_block": 56,
  "virtual_op": 0
}
2018/02/11 12:02:00
idfollow
json["follow",{"follower":"caesium133","following":"corn113","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775469/Trx f5a438b0277ad90727241bc1d1ffa5e97cc864dd
View Raw JSON Data
{
  "block": 19775469,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"corn113\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:00",
  "trx_id": "f5a438b0277ad90727241bc1d1ffa5e97cc864dd",
  "trx_in_block": 39,
  "virtual_op": 0
}
2018/02/11 12:02:00
idfollow
json["follow",{"follower":"caesium133","following":"crackcake","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775469/Trx 53d52fea3bedfb802e88f275eef7eda5fec25a28
View Raw JSON Data
{
  "block": 19775469,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"crackcake\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:00",
  "trx_id": "53d52fea3bedfb802e88f275eef7eda5fec25a28",
  "trx_in_block": 29,
  "virtual_op": 0
}
2018/02/11 12:02:00
idfollow
json["follow",{"follower":"caesium133","following":"crawfish37","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775469/Trx b9ec5e4998190d713ac160334e42e3645dc4c594
View Raw JSON Data
{
  "block": 19775469,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"crawfish37\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:00",
  "trx_id": "b9ec5e4998190d713ac160334e42e3645dc4c594",
  "trx_in_block": 18,
  "virtual_op": 0
}
2018/02/11 12:02:00
idfollow
json["follow",{"follower":"caesium133","following":"crowsaint","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775469/Trx 3c36b7bb3c0c174d4731d2124eb0b8083da14c8d
View Raw JSON Data
{
  "block": 19775469,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"crowsaint\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:00",
  "trx_id": "3c36b7bb3c0c174d4731d2124eb0b8083da14c8d",
  "trx_in_block": 14,
  "virtual_op": 0
}
2018/02/11 12:02:00
idfollow
json["follow",{"follower":"caesium133","following":"cryptoboy516","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775469/Trx 9323857bea6cee6278248d3e32a8e4e8972e2158
View Raw JSON Data
{
  "block": 19775469,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"cryptoboy516\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:02:00",
  "trx_id": "9323857bea6cee6278248d3e32a8e4e8972e2158",
  "trx_in_block": 2,
  "virtual_op": 0
}
2018/02/11 12:01:57
idfollow
json["follow",{"follower":"caesium133","following":"cryptogirl1","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775468/Trx 8f6f05b5ed13fee8b89dd117be26d8338d2af259
View Raw JSON Data
{
  "block": 19775468,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"cryptogirl1\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:57",
  "trx_id": "8f6f05b5ed13fee8b89dd117be26d8338d2af259",
  "trx_in_block": 30,
  "virtual_op": 0
}
2018/02/11 12:01:57
idfollow
json["follow",{"follower":"caesium133","following":"cryptokas","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775468/Trx 973e1e160859755d58b71494439fb4da152b7ddb
View Raw JSON Data
{
  "block": 19775468,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"cryptokas\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:57",
  "trx_id": "973e1e160859755d58b71494439fb4da152b7ddb",
  "trx_in_block": 11,
  "virtual_op": 0
}
2018/02/11 12:01:54
idfollow
json["follow",{"follower":"caesium133","following":"cryptoriddler","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775467/Trx 0fa5e69f41f542d66f3a726aed2b53840f841687
View Raw JSON Data
{
  "block": 19775467,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"cryptoriddler\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:54",
  "trx_id": "0fa5e69f41f542d66f3a726aed2b53840f841687",
  "trx_in_block": 66,
  "virtual_op": 0
}
2018/02/11 12:01:54
idfollow
json["follow",{"follower":"caesium133","following":"cyan2017","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775467/Trx 76edfe92dc1dd09737b0e35f1d751e8954f458b9
View Raw JSON Data
{
  "block": 19775467,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"cyan2017\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:54",
  "trx_id": "76edfe92dc1dd09737b0e35f1d751e8954f458b9",
  "trx_in_block": 33,
  "virtual_op": 0
}
2018/02/11 12:01:54
idfollow
json["follow",{"follower":"caesium133","following":"d-m","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775467/Trx 48f42e8ae0a4dc1565c57ab2478ddaef39c47584
View Raw JSON Data
{
  "block": 19775467,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"d-m\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:54",
  "trx_id": "48f42e8ae0a4dc1565c57ab2478ddaef39c47584",
  "trx_in_block": 15,
  "virtual_op": 0
}
2018/02/11 12:01:54
idfollow
json["follow",{"follower":"caesium133","following":"d7795","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775467/Trx 904218e68493246c871e7ad5e7a196fadbd76c3c
View Raw JSON Data
{
  "block": 19775467,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"d7795\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:54",
  "trx_id": "904218e68493246c871e7ad5e7a196fadbd76c3c",
  "trx_in_block": 4,
  "virtual_op": 0
}
2018/02/11 12:01:51
idfollow
json["follow",{"follower":"caesium133","following":"dakfn","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775466/Trx 9ecce763a6043e8d918788ca425584eaad6b32c5
View Raw JSON Data
{
  "block": 19775466,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"dakfn\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:51",
  "trx_id": "9ecce763a6043e8d918788ca425584eaad6b32c5",
  "trx_in_block": 45,
  "virtual_op": 0
}
2018/02/11 12:01:51
idfollow
json["follow",{"follower":"caesium133","following":"dabok","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775466/Trx 34b52bdbd367341d87a1c2ddc114855673dc9710
View Raw JSON Data
{
  "block": 19775466,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"dabok\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:51",
  "trx_id": "34b52bdbd367341d87a1c2ddc114855673dc9710",
  "trx_in_block": 41,
  "virtual_op": 0
}
2018/02/11 12:01:51
idfollow
json["follow",{"follower":"caesium133","following":"danbain","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775466/Trx 4e8a30dc4b9395b69e4933f3476190461c80d553
View Raw JSON Data
{
  "block": 19775466,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"danbain\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:51",
  "trx_id": "4e8a30dc4b9395b69e4933f3476190461c80d553",
  "trx_in_block": 30,
  "virtual_op": 0
}
2018/02/11 12:01:51
idfollow
json["follow",{"follower":"caesium133","following":"danihwang","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775466/Trx 0ce4ceee4b29199b8de6f2b738793b5b09b04e35
View Raw JSON Data
{
  "block": 19775466,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"danihwang\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:51",
  "trx_id": "0ce4ceee4b29199b8de6f2b738793b5b09b04e35",
  "trx_in_block": 9,
  "virtual_op": 0
}
2018/02/11 12:01:48
idfollow
json["follow",{"follower":"caesium133","following":"dayoung","what":["blog"]}]
required auths[]
required posting auths["caesium133"]
Transaction InfoBlock #19775465/Trx 2a4da8e3cab0ded537765b5919214c9c2daa3e0d
View Raw JSON Data
{
  "block": 19775465,
  "op": [
    "custom_json",
    {
      "id": "follow",
      "json": "[\"follow\",{\"follower\":\"caesium133\",\"following\":\"dayoung\",\"what\":[\"blog\"]}]",
      "required_auths": [],
      "required_posting_auths": [
        "caesium133"
      ]
    }
  ],
  "op_in_trx": 0,
  "timestamp": "2018-02-11T12:01:48",
  "trx_id": "2a4da8e3cab0ded537765b5919214c9c2daa3e0d",
  "trx_in_block": 46,
  "virtual_op": 0
}

Account Metadata

POSTING JSON METADATA
profile{"location":"Republic of Korea","website":"https://parksb.github.io/","profile_image":"https://i.imgur.com/PyMw8Cb.png","name":"Cs133","about":"Developer & Designer"}
JSON METADATA
profile{"location":"Republic of Korea","website":"https://parksb.github.io/","profile_image":"https://i.imgur.com/PyMw8Cb.png","name":"Cs133","about":"Developer & Designer"}
{
  "posting_json_metadata": {
    "profile": {
      "location": "Republic of Korea",
      "website": "https://parksb.github.io/",
      "profile_image": "https://i.imgur.com/PyMw8Cb.png",
      "name": "Cs133",
      "about": "Developer & Designer"
    }
  },
  "json_metadata": {
    "profile": {
      "location": "Republic of Korea",
      "website": "https://parksb.github.io/",
      "profile_image": "https://i.imgur.com/PyMw8Cb.png",
      "name": "Cs133",
      "about": "Developer & Designer"
    }
  }
}

Auth Keys

Owner
Single Signature
Public Keys
STM6uqvzdojd3tWQMLxrgkFJqrvBCcNU25b5fLirKTeLZgZjdznAh1/1
Active
Single Signature
Public Keys
STM4tf95EmPxwCny9Mp4qjVmnGLMGKNMmbk1jzgZQd7jH5QFAAZJV1/1
Posting
Single Signature
Public Keys
STM5VPFtrdSdJTUoEDiMGVqeviTpQVng7VhXGr2fkGy2PJNWdj97K1/1
Memo
STM6vCF37qihYRr2AAjPRvgzctQUz4MfKkWA8s91KWTprv6XCSKVT
{
  "owner": {
    "account_auths": [],
    "key_auths": [
      [
        "STM6uqvzdojd3tWQMLxrgkFJqrvBCcNU25b5fLirKTeLZgZjdznAh",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "active": {
    "account_auths": [],
    "key_auths": [
      [
        "STM4tf95EmPxwCny9Mp4qjVmnGLMGKNMmbk1jzgZQd7jH5QFAAZJV",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "posting": {
    "account_auths": [],
    "key_auths": [
      [
        "STM5VPFtrdSdJTUoEDiMGVqeviTpQVng7VhXGr2fkGy2PJNWdj97K",
        1
      ]
    ],
    "weight_threshold": 1
  },
  "memo": "STM6vCF37qihYRr2AAjPRvgzctQUz4MfKkWA8s91KWTprv6XCSKVT"
}

Witness Votes

0 / 30
No active witness votes.
[]