2017λ μ΄, μ°λ¦¬λ κΈ°μ‘΄ P2P λ©μ μ μ λΉν΄ μ₯μ μ λ Όμνλ©΄μ λΈλ‘μ²΄μΈ λ©μ μ (μ΄λ¦κ³Ό λ§ν¬λ νλ‘νμ μμ)λ₯Ό λ§λ€κΈ° μμνμ΅λλ€.
ν©κ²© 2.5 μλ
κ°μ λ
Έλ ₯ λμ μ°λ¦¬λ μ°λ¦¬μ κ°λ
μ μ
μ¦ν μ μμμ΅λλ€. μ΄μ iOSμ© λ©μ μ μ±κ³Ό μΉ PWAλ₯Ό μ¬μ©ν μ μμ΅λλ€. Windows, GNU/Linuxλ§₯ OS λ° Android.
μ€λμ λΈλ‘μ²΄μΈ λ©μ μ μ μλ λ°©μκ³Ό ν΄λΌμ΄μΈνΈ μ ν리μΌμ΄μ
μ΄ ν΄λΉ APIμ μλνλ λ°©μμ μλ €λλ¦¬κ² μ΅λλ€.

μ°λ¦¬λ λΈλ‘체μΈμ΄ κΈ°μ‘΄ P2P λ©μ μ μ 보μ λ° κ°μΈ μ 보 λ³΄νΈ λ¬Έμ λ₯Ό ν΄κ²°νκΈ°λ₯Ό μνμ΅λλ€.
- ν λ²μ ν΄λ¦μΌλ‘ κ³μ μ λ§λ€ μ μμ΅λλ€. μ νλ μ΄λ©μΌμ΄ μκ³ μ£Όμλ‘μ΄λ μ§λ¦¬μ μμΉμ μ‘μΈμ€ν μ μμ΅λλ€.
- λλ΄μλ μ λ μ§μ μ°κ²°μ μ€μ νμ§ μμΌλ©° λͺ¨λ ν΅μ μ λΆμ° λ Έλ μμ€ν μ ν΅ν΄ μ΄λ£¨μ΄μ§λλ€. μ¬μ©μμ IP μ£Όμλ μλ‘ μ κ·Όν μ μμ΅λλ€.
- λͺ¨λ λ©μμ§λ end-to-end curve25519xsalsa20poly1305λ‘ μνΈνλ©λλ€. μ΄κ²μ λꡬμκ²λ λλΌμ§ μμ κ² κ°μ§λ§ μ°λ¦¬μ μμ€ μ½λλ 곡κ°λμ΄ μμ΅λλ€.
- MITM 곡격μ μ μΈλ©λλ€. κ° λ©μμ§λ νΈλμμ μ΄λ©° Ed25519 EdDSAμ μν΄ μλͺ λ©λλ€.
- λ©μμ§λ μ체 λΈλ‘μΌλ‘ λλ©λλ€. μΌκ΄μ±κ³Ό
timestampλΈλ‘μ μμ ν μ μμΌλ―λ‘ λ©μμ§ μμλ μμ ν μ μμ΅λλ€. - "λλ κ·Έλ° λ§μ νμ§ μμμ΅λλ€"λ λΈλ‘체μΈμ λ©μμ§μλ μ μ©λμ§ μμ΅λλ€.
- λ©μμ§μ "μ§μμ±"μ νμΈνλ μ€μ ꡬ쑰λ μμ΅λλ€. μ΄λ ν©μμ κΈ°λ°ν λΆμ° λ Έλ μμ€ν μ μν΄ μνλλ©°, μ΄λ μ¬μ©μκ° μμ ν©λλ€.
- κ²μ΄ λΆκ°λ₯ - κ³μ μ μ°¨λ¨ν μ μμΌλ©° λ©μμ§λ₯Ό μμ ν μ μμ΅λλ€.
- λΈλ‘μ²΄μΈ 2FAλ SMSλ₯Ό ν΅ν μ§μ₯ κ°μ 2FAμ λμμ λλ€.
- μΈμ λ μ§ λͺ¨λ μ₯μΉμμ λͺ¨λ λνλ₯Ό μ»μ μ μλ€λ κ²μ λνλ₯Ό λ‘컬μ μ ν μ μ₯ν νμκ° μλ€λ κ²μ μλ―Έν©λλ€.
- λ©μμ§ μ λ¬ νμΈ. μ¬μ©μμ μ₯μΉκ° μλ λ€νΈμν¬μ λν κ²μ λλ€. κΈ°λ³Έμ μΌλ‘ μ΄λ μμ μκ° λ©μμ§λ₯Ό μ½μ μ μλμ§ νμΈνλ κ²μ λλ€. μ΄λ μ€μν μλ¦Όμ 보λ΄λ λ° μ μ©ν κΈ°λ₯μ λλ€.
λΈλ‘체μΈμ μ΄μ μλ μνΈννμΈ Ethereum, Dogecoin, Lisk, Dash, Bitcoin(μμ§ μ§ν μ€)κ³Όμ κΈ΄λ°ν ν΅ν© λ° μ±ν μμ ν ν°μ λ³΄λΌ μ μλ κΈ°λ₯λ ν¬ν¨λ©λλ€. μ°λ¦¬λ λ΄μ₯ν μνΈν κ΅νκΈ°λ λ§λ€μμ΅λλ€.
κ·Έλ¦¬κ³ λͺ¨λ κ²μ΄ μ΄λ»κ² μλνλμ§.
λ©μμ§λ κ±°λμ΄λ€
λͺ¨λ μ¬λμ μ΄λ―Έ λΈλ‘체μΈμ κ±°λκ° ν μ¬μ©μμμ λ€λ₯Έ μ¬μ©μλ‘ ν ν°(μ½μΈ)μ μ μ‘νλ€λ μ¬μ€μ μ΅μν©λλ€. λΉνΈμ½μΈμ²λΌ. μ°λ¦¬λ λ©μμ§ μ μ‘μ μν΄ νΉλ³ν μ νμ νΈλμμ μ λ§λ€μμ΅λλ€.
λΈλ‘체μΈμ λ©μ μ λ‘ λ©μμ§λ₯Ό 보λ΄λ €λ©΄ λ€μκ³Ό κ°μ λͺ κ°μ§ λ¨κ³λ₯Ό κ±°μ³μΌ ν©λλ€.
- λ©μμ§ ν μ€νΈ μνΈν
- μνΈλ¬Έμ νΈλμμ μ λ£κΈ°
- κ±°λμ μλͺ νμΈμ
- λͺ¨λ λ€νΈμν¬ λ Έλμ νΈλμμ 보λ΄κΈ°
- λ Έλμ λΆμ° μμ€ν μ΄ λ©μμ§μ "μ§μμ±"μ κ²°μ ν©λλ€.
- λͺ¨λ κ²μ΄ μ μμ΄λ©΄ λ©μμ§κ° ν¬ν¨λ νΈλμμ μ΄ λ€μ λΈλ‘μ ν¬ν¨λ©λλ€.
- μμ μλ λ©μμ§ νΈλμμ μ κ²μνκ³ μνΈλ₯Ό ν΄λ ν©λλ€.
1~3λ¨κ³μ 7λ¨κ³λ ν΄λΌμ΄μΈνΈμμ λ‘μ»¬λ‘ μνλκ³ 5~6λ¨κ³λ νΈμ€νΈμμ μνλ©λλ€.
λ©μμ§ μνΈν
λ©μμ§λ λ³΄λΈ μ¬λμ κ°μΈ ν€μ λ°λ μ¬λμ κ³΅κ° ν€λ‘ μνΈνλ©λλ€. μ°λ¦¬λ λ€νΈμν¬μμ κ³΅κ° ν€λ₯Ό κ°μ Έμ€μ§λ§ μ΄λ₯Ό μν΄μλ μμ μμ κ³μ μ μ΄κΈ°νν΄μΌ ν©λλ€. μ¦, μ μ΄λ νλμ κ±°λκ° μμ΄μΌ ν©λλ€. REST μμ²μ μ¬μ©ν μ μμ΅λλ€ GET /api/accounts/getPublicKey?address={ADAMANT address}, μ±ν
μ λ‘λν λ λν μλμ κ³΅κ° ν€λ₯Ό μ΄λ―Έ μ¬μ©ν μ μμ΅λλ€.

λ©μ μ λ curve25519xsalsa20poly1305 μκ³ λ¦¬μ¦μ μ¬μ©νμ¬ λ©μμ§λ₯Ό μνΈνν©λλ€(). κ³μ μ Ed25519 ν€κ° ν¬ν¨λμ΄ μμΌλ―λ‘ μμλ₯Ό νμ±νλ €λ©΄ λ¨Όμ ν€λ₯Ό Curve25519 Diffie-HellmanμΌλ‘ λ³νν΄μΌ ν©λλ€.
λ€μμ JavaScriptμ μμ λλ€.
/**
* Encodes a text message for sending to ADM
* @param {string} msg message to encode
* @param {*} recipientPublicKey recipient's public key
* @param {*} privateKey our private key
* @returns {{message: string, nonce: string}}
*/
adamant.encodeMessage = function (msg, recipientPublicKey, privateKey) {
const nonce = Buffer.allocUnsafe(24)
sodium.randombytes(nonce)
if (typeof recipientPublicKey === 'string') {
recipientPublicKey = hexToBytes(recipientPublicKey)
}
const plainText = Buffer.from(msg)
const DHPublicKey = ed2curve.convertPublicKey(recipientPublicKey)
const DHSecretKey = ed2curve.convertSecretKey(privateKey)
const encrypted = nacl.box(plainText, nonce, DHPublicKey, DHSecretKey)
return {
message: bytesToHex(encrypted),
nonce: bytesToHex(nonce)
}
}λ©μμ§λ‘ νΈλμμ νμ±
νΈλμμ μ μΌλ°μ μΈ κ΅¬μ‘°λ λ€μκ³Ό κ°μ΅λλ€.
{
"id": "15161295239237781653",
"height": 7585271,
"blockId": "16391508373936326027",
"type": 8,
"block_timestamp": 45182260,
"timestamp": 45182254,
"senderPublicKey": "bd39cc708499ae91b937083463fce5e0668c2b37e78df28f69d132fce51d49ed",
"senderId": "U16023712506749300952",
"recipientId": "U17653312780572073341",
"recipientPublicKey": "23d27f616e304ef2046a60b762683b8dabebe0d8fc26e5ecdb1d5f3d291dbe21",
"amount": 204921300000000,
"fee": 50000000,
"signature": "3c8e551f60fedb81e52835c69e8b158eb1b8b3c89a04d3df5adc0d99017ffbcb06a7b16ad76d519f80df019c930960317a67e8d18ab1e85e575c9470000cf607",
"signatures": [],
"confirmations": 3660548,
"asset": {}
} λ©μμ§ κ±°λμμ κ°μ₯ μ€μν κ²μ asset - κ°μ²΄μ λ©μμ§λ₯Ό λ£μ΄μΌ ν©λλ€. chat ꡬ쑰:
message- μνΈνλ λ©μμ§λ₯Ό μ μ₯νμΈμown_message-λͺ©νtypeβ λ©μμ§ μ ν
λ©μμ§λ μ νμΌλ‘ ꡬλΆλ©λλ€. κΈ°λ³Έμ μΌλ‘ λ§€κ°λ³μλ type μ΄ν΄νλ λ°©λ²μ μλ €μ€λ€ message. λ¬Έμλ§ λ³΄λΌ μλ μκ³ λ΄λΆμ ν₯λ―Έλ‘μ΄ λ΄μ©μ΄ ν¬ν¨λ κ°μ²΄λ₯Ό λ³΄λΌ μλ μμ΅λλ€. μλ₯Ό λ€μ΄ λ©μ μ κ° μ±ν
μμ μνΈννλ₯Ό μ μ‘νλ λ°©μμ
λλ€.
κ²°κ³Όμ μΌλ‘ μ°λ¦¬λ νΈλμμ μ μμ±ν©λλ€.
{
"transaction": {
"type": 8,
"amount": 0,
"senderId": "U12499126640447739963",
"senderPublicKey": "e9cafb1e7b403c4cf247c94f73ee4cada367fcc130cb3888219a0ba0633230b6",
"asset": {
"chat": {
"message": "cb682accceef92d7cddaaddb787d1184ab5428",
"own_message": "e7d8f90ddf7d70efe359c3e4ecfb5ed3802297b248eacbd6",
"type": 1
}
},
"recipientId": "U15677078342684640219",
"timestamp": 63228087,
"signature": "ΡΡΡ Π±ΡΠ΄Π΅Ρ ΠΏΠΎΠ΄ΠΏΠΈΡΡ"
}
}κ±°λ μλͺ
λͺ¨λ μ¬λμ΄ λ³΄λΈ μ¬λκ³Ό λ°λ μ¬λμ μ§μ μ¬λΆ, λ³΄λΈ μκ°, λ©μμ§ λ΄μ©μ νμ ν μ μλλ‘ κ±°λμ μλͺ ν©λλ€. λμ§νΈ μλͺ μ μ¬μ©νλ©΄ κ³΅κ° ν€λ₯Ό μ¬μ©νμ¬ κ±°λμ μ§μ μ¬λΆλ₯Ό νμΈν μ μμ΅λλ€. μ΄λ₯Ό μν΄μλ κ°μΈ ν€κ° νμνμ§ μμ΅λλ€.
κ·Έλ¬λ μλͺ μ체λ κ°μΈ ν€λ₯Ό μ¬μ©νμ¬ μνλ©λλ€.

λ€μ΄μ΄κ·Έλ¨μ λ¨Όμ SHA-256μΌλ‘ νΈλμμ
μ ν΄μν λ€μ μλͺ
νλ κ²μ 보μ¬μ€λλ€. κ·Έλ¦¬κ³ μ¬μΈμ λ°μ signature, νΈλμμ
IDλ SHA-256 ν΄μμ μΌλΆμ
λλ€.
ꡬν μ:
1 β λ©μμ§λ₯Ό ν¬ν¨ν λ°μ΄ν° λΈλ‘μ νμ±ν©λλ€.
/**
* Calls `getBytes` based on transaction type
* @see privateTypes
* @implements {ByteBuffer}
* @param {transaction} trs
* @param {boolean} skipSignature
* @param {boolean} skipSecondSignature
* @return {!Array} Contents as an ArrayBuffer.
* @throws {error} If buffer fails.
*/
adamant.getBytes = function (transaction) {
...
switch (transaction.type) {
case constants.Transactions.SEND:
break
case constants.Transactions.CHAT_MESSAGE:
assetBytes = this.chatGetBytes(transaction)
assetSize = assetBytes.length
break
β¦
default:
alert('Not supported yet')
}
var bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 64 + 64 + assetSize, true)
bb.writeByte(transaction.type)
bb.writeInt(transaction.timestamp)
...
bb.flip()
var arrayBuffer = new Uint8Array(bb.toArrayBuffer())
var buffer = []
for (var i = 0; i < arrayBuffer.length; i++) {
buffer[i] = arrayBuffer[i]
}
return Buffer.from(buffer)
}
2 - λ°μ΄ν° λΈλ‘μμ SHA-256 κ³μ°
/**
* Creates hash based on transaction bytes.
* @implements {getBytes}
* @implements {crypto.createHash}
* @param {transaction} trs
* @return {hash} sha256 crypto hash
*/
adamant.getHash = function (trs) {
return crypto.createHash('sha256').update(this.getBytes(trs)).digest()
}3 β κ±°λ μλͺ
adamant.transactionSign = function (trs, keypair) {
var hash = this.getHash(trs)
return this.sign(hash, keypair).toString('hex')
}
/**
* Creates a signature based on a hash and a keypair.
* @implements {sodium}
* @param {hash} hash
* @param {keypair} keypair
* @return {signature} signature
*/
adamant.sign = function (hash, keypair) {
return sodium.crypto_sign_detached(hash, Buffer.from(keypair.privateKey, 'hex'))
}λ©μμ§μ ν¨κ» νΈλμμ μ λ€νΈμν¬ λ Έλλ‘ λ³΄λ΄κΈ°
λ€νΈμν¬κ° λΆμ°λμ΄ μμΌλ―λ‘ κ°λ°©ν APIλ₯Ό μ¬μ©νλ λͺ¨λ λ
Έλκ° κ°λ₯ν©λλ€. μλν¬μΈνΈμ POST μμ²νκΈ° api/transactions:
curl 'api/transactions' -X POST
-d 'TX_DATA'μ΄μ λν μλ΅μΌλ‘ μ°λ¦¬λ λ€μ μ νμ κ±°λ IDλ₯Ό λ°κ² λ©λλ€.
{
"success": true,
"nodeTimestamp": 63228852,
"transactionId": "6146865104403680934"
}κ±°λ κ²μ¦
ν©μλ₯Ό κΈ°λ°μΌλ‘ νλ λΆμ° λ Έλ μμ€ν μ κ±°λ λ©μμ§μ "μ§μμ±"μ κ²°μ ν©λλ€. λꡬμκ², λꡬμκ², μΈμ , λ©μμ§κ° λ€λ₯Έ λ©μμ§λ‘ λ체λμλμ§, μ μ‘ μκ°μ΄ μ¬λ°λ₯΄κ² νμλμλμ§ μ¬λΆ. μ΄λ λΈλ‘체μΈμ λ§€μ° μ€μν μ₯μ μ λλ€. κ²μ¦μ λ΄λΉνλ μ€μ κ΅¬μ‘°κ° μμΌλ©° λ©μμ§ μμμ λ΄μ©μ΄ μμ‘°λ μ μμ΅λλ€.
λ¨Όμ , ν λ Έλκ° μ νμ±μ νμΈν λ€μ μ΄λ₯Ό λ€λ₯Έ λ Έλμκ² λ³΄λ λλ€. λλ€μκ° λͺ¨λ κ²μ΄ μ μμ΄λΌκ³ λ§νλ©΄ νΈλμμ μ 체μΈμ λ€μ λΈλ‘μ ν¬ν¨λ©λλ€. μ΄κ²μ΄ ν©μμ λλ€.

κ²μ¬λ₯Ό λ΄λΉνλ λ
Έλ μ½λ λΆλΆμ GitHubμμ λ³Ό μ μμ΅λλ€. ΠΈ . λ€, λ
Έλλ Node.jsμμ μ€νλ©λλ€.
λΈλ‘μ λ©μμ§κ° ν¬ν¨λ νΈλμμ ν¬ν¨
ν©μμ λλ¬νλ©΄ λ©μμ§κ° ν¬ν¨λ νΈλμμ μ΄ λ€λ₯Έ μ ν¨ν νΈλμμ κ³Ό ν¨κ» λ€μ λΈλ‘μ ν¬ν¨λ©λλ€.
λΈλ‘μ μ격ν μμλ₯Ό κ°μ§λ©°, κ° νμ λΈλ‘μ μ΄μ λΈλ‘μ ν΄μλ₯Ό κΈ°λ°μΌλ‘ νμ±λ©λλ€.

μμ μ μ°λ¦¬μ λ©μμ§λ μ΄ μμμ ν¬ν¨λμ΄ μμΌλ©° "μ¬λ°°μ΄"λ μ μλ€λ κ²μ
λλ€. μ¬λ¬ λ©μμ§κ° νλμ λΈλ‘μ μνλ κ²½μ° μμλ λ€μκ³Ό κ°μ΄ κ²°μ λ©λλ€. timestamp λ©μμ§.
λ©μμ§ μ½κΈ°
λ©μ μ μ ν리μΌμ΄μ
μ μμ μμκ² μ μ‘λ λΈλ‘체μΈμμ νΈλμμ
μ κ²μν©λλ€. μ΄λ₯Ό μν΄ μ°λ¦¬λ μλν¬μΈνΈλ₯Ό λ§λ€μμ΅λλ€. api/chatrooms.
λͺ¨λ κ±°λλ λꡬλ μ΄μ©ν μ μμ΅λλ€. μνΈνλ λ©μμ§λ₯Ό λ°μ μ μμ΅λλ€. κ·Έλ¬λ μμ μλ§μ΄ μμ μ κ°μΈ ν€μ λ°μ μμ κ³΅κ° ν€λ₯Ό μ¬μ©νμ¬ μνΈλ₯Ό ν΄λ ν μ μμ΅λλ€.
**
* Decodes the incoming message
* @param {any} msg encoded message
* @param {string} senderPublicKey sender public key
* @param {string} privateKey our private key
* @param {any} nonce nonce
* @returns {string}
*/
adamant.decodeMessage = function (msg, senderPublicKey, privateKey, nonce) {
if (typeof msg === 'string') {
msg = hexToBytes(msg)
}
if (typeof nonce === 'string') {
nonce = hexToBytes(nonce)
}
if (typeof senderPublicKey === 'string') {
senderPublicKey = hexToBytes(senderPublicKey)
}
if (typeof privateKey === 'string') {
privateKey = hexToBytes(privateKey)
}
const DHPublicKey = ed2curve.convertPublicKey(senderPublicKey)
const DHSecretKey = ed2curve.convertSecretKey(privateKey)
const decrypted = nacl.box.open(msg, nonce, DHPublicKey, DHSecretKey)
return decrypted ? decode(decrypted) : ''
}λ λμΌ?
λ©μμ§λ μ½ 5μ΄ μμ μ΄λ° λ°©μμΌλ‘ μ λ¬λλ―λ‘(μ΄λκ° μλ‘μ΄ λ€νΈμν¬ λΈλ‘μ΄ λνλλ μκ°μ) ν΄λΌμ΄μΈνΈ-λ Έλ λ° λ Έλ-λ Έλ μμΌ μ°κ²°μ μκ°ν΄ λμ΅λλ€. λ Έλκ° μλ‘μ΄ νΈλμμ μ μμ νλ©΄ μ ν¨μ±μ νμΈνκ³ μ΄λ₯Ό λ€λ₯Έ λ Έλμ μ λ¬ν©λλ€. ν©μκ° μ΄λ£¨μ΄μ§κ³ λΈλ‘μ ν¬ν¨λκΈ° μ μλ λ©μ μ ν΄λΌμ΄μΈνΈλ νΈλμμ μ μ¬μ©ν μ μμ΅λλ€. μ΄λ κ² νλ©΄ μΌλ° μΈμ€ν΄νΈ λ©μ μ μ²λΌ μ¦μ λ©μμ§λ₯Ό μ λ¬ν μ μμ΅λλ€.
μ£Όμλ‘μ μ μ₯νκΈ° μν΄ KVS(Key-Value Storage)λ₯Ό λ§λ€μμ΅λλ€. μ΄λ λ λ€λ₯Έ μ νμ νΈλμμ
μ
λλ€. asset μνΈνλ NaCl-boxλ μλμ§λ§ . μ΄κ²μ΄ λ©μ μ κ° λ€λ₯Έ λ°μ΄ν°λ₯Ό μ μ₯νλ λ°©λ²μ
λλ€.
νμΌ/μ΄λ―Έμ§ μ μ‘ λ° κ·Έλ£Ή μ±ν μλ μ¬μ ν λ§μ μμ μ΄ νμν©λλ€. λ¬Όλ‘ , μ€μμ μ€μ νμμμλ μ΄κ²μ΄ λΉ λ₯΄κ² "λ§κ°μ§" μ μμ§λ§, μ°λ¦¬λ λμΌν μμ€μ κ°μΈ μ 보 보νΈλ₯Ό μ μ§νκ³ μΆμ΅λλ€.
μ, μμ§ ν΄μΌ ν μΌμ΄ μμ΅λλ€. μ΄μμ μΌλ‘λ μ€μ κ°μΈ μ 보 보νΈλ μ¬μ©μκ° κ³΅μ© λ€νΈμν¬ λ Έλμ μ°κ²°νμ§ μκ³ μ€μ€λ‘ λ Έλλ₯Ό μμ±ν κ²μ΄λΌκ³ κ°μ ν©λλ€. λͺ νΌμΌνΈμ μ¬μ©μκ° μ΄ μμ μ μννλ€κ³ μκ°νμλμ? λ§μ΅λλ€, 0. μ ν¬λ Tor λ²μ μ λ©μ μ λ‘ μ΄ λ¬Έμ λ₯Ό λΆλΆμ μΌλ‘ ν΄κ²°ν μ μμμ΅λλ€.
μ°λ¦¬λ λΈλ‘체μΈμ λ©μ μ κ° μ‘΄μ¬ν μ μλ€λ κ²μ μ¦λͺ νμ΅λλ€. μ΄μ μλ 2012λ μ λ¨ ν λ²μ μλλ§ μμμ΅λλ€. , κΈ΄ λ©μμ§ μ λ¬ μκ°, CPU λΆν, λͺ¨λ°μΌ μ ν리μΌμ΄μ λΆμ‘±μΌλ‘ μΈν΄ μ€ν¨νμ΅λλ€.
κ·Έλ¦¬κ³ νμλ‘ μ λΈλ‘체μΈμ λ©μ μ κ° μλλ₯Ό μμ μλ€λ μ¬μ€μ κΈ°μΈν©λλ€. μ¬λλ€μ μμ μ κ³μ μ λν΄ μ±
μμ μ§ μ€λΉκ° λμ΄ μμ§ μκ³ , κ°μΈ μ 보λ₯Ό μμ νλ κ²μ΄ μμ§ μΆμΈκ° μλλ©°, κΈ°μ μ΄ λΈλ‘체μΈμμ λΉ λ₯Έ μλλ₯Ό νμ©νμ§ μλλ€λ μ¬μ€μ κΈ°μΈν©λλ€. μ°λ¦¬ νλ‘μ νΈμ λ λ§μ κΈ°μ μ μ μ¬μ μ΄ λ€μμ λνλ κ²μ
λλ€. λΉμ μ λ³Ό μ.
μΆμ² : habr.com
