- ๐ฏ I majored in Electronic Engineering
- ๐ซ Konkuk University (2015.03 ~ 2021.08)
brandnew-one / til Goto Github PK
View Code? Open in Web Editor NEWToday I Learned
Today I Learned
๋ฏธ๋ฃจ๊ณ ๋ฏธ๋ฃจ๋ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ํด์ ๋๋์ด ํ๋ฒ ์์ธํ๊ฒ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค!
๊ฐ์์ ๋ธ๋ก๊ทธ๋ฅผ ํ์ฉํด์ ํ๋ฐ์ฑ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ง ๋ฏธ๋์ ๋๋ฅผ ์ํด ์ต๋ํ ์์ธํ๊ณ ์ดํดํ๊ธฐ ์ฝ๊ฒ ํ์ด๋ณด๋ ค๊ณ ํ๋ค
๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ด ํ์ํ ์ด์ ๋ ๋จ์ํ๋ค. ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ์ด์ฉํ์ง ์๊ณ ๋ฉ์ธ ์ฐ๋ ๋์์ ๋ชจ๋ ์์
์ ์ํํ๊ฒ ๋๋ฉด ๋๋ฆฌ๋ค.
์๋ฅผ ๋ค๋ฉด ๋คํธ์ํฌ ํต์ ์ ํตํด ๋ถ๋ฌ์จ ๊ฐ์ ํ
์ด๋ธ ๋ทฐ์ ๋ณด์ฌ์ค ๋ ์คํฌ๋กค์ ํ๋ฉด ์ฑ์ด ๋๋ ๋๊ธฐ๋ ํ์์ด๋ค.
๊ทธ๋ผ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ์ด์ฉํ๋ฉด ์์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๋๊ฑธ๊น?
๋ฉ์ธ ์ฐ๋ ๋์์ ๋ชจ๋ ์์
์ ์ฒ๋ฆฌํ๋๊ฒ์ ์ค์ํ๋ก ์๋ฅผ๋ค๋ฉด ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค
๋ถ์ํ ๋ ธ๋์ ํ๋ช ๋ง ์ด์ฌํ ์ผ์ ํ๊ณ ์๋ค. ๋ ๋ชป์ฐธ์!!!
๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ํ์ฉํ๋ฉด ์์ ๊ฐ์ด ํ ๋ ธ๋์์๊ฒ ์์ ์ด ๋ชฐ๋ฆฌ์ง ์๊ณ ์ด๋ฅผ ์ ์ ํ๊ฒ ๋์์ ์ํํ ์ ์๋๋ก ์์ ์ ๋ถ๋ฐฐํ ์ ์๋ค
์ ๋ง ๊ฐ์ฌํ๊ฒ๋ ์ฐ๋ฆฌ๋ ์ง์ ์ ์ผ๋ก ์ฐ๋ ๋๋ฅผ ๊ด๋ฆฌํ์ง ์๊ณ , iOS์์ ์ ๊ณตํ๋ ๋๊ธฐํ๋ ฌ(Queue)์ ๋ณด๋ด๊ธฐ๋ง ํ๋ฉด ๋๋ค.
- GCD(=Dispatch Queue)
- Operation Queue
iOS์์ ์ ๊ณตํ๋ ๋๊ธฐํ๋ ฌ์ ํฌ๊ฒ ์์ ๋ ์ข
๋ฅ์ด๋ค!
์ฐ๋ฆฌ๋ ์์ ๋ค์ ์์ ํ์ ๋ฃ์ด์ฃผ๋ฉด ๊ฐ ํ์ ํน์ฑ์ ๋ง๊ฒ OS์์ ๊ด๋ฆฌํด์ค๋ค!!
DispatchQueue.global().async {
print("Task1")
print("----------Task1---------")
}
์ฌ์ฉ์์๋ฅผ ๋ณด๋ฉด
DispatchQueue(ํ์ ๋ณด๋ธ๋ค.)
.global()(global ์ด๋ผ๋ ํ์)
.async (๋น๋๊ธฐ์ ์ผ๋ก)
{ ์ดํ ์์
์
} ์ผ๋ก ์ดํดํ ์ ์๋ค.
์ฒ์ ๊ณต๋ถํ ๋ ๋๋ฅผ ๋ฉ๋ถ์ ๋น ํธ๋ ธ๋ ๋๊ธฐ/๋น๋๊ธฐ
ํค์๋๊ฐ ๋ณด์ด๋๋ฐ ์ฒ์ฒํ ํ๋์ฉ ํ์ด๊ฐ๋ณด์!
๋๋๊ฒ๋ ์ด์ ์ ๋๊ธฐ์ ๋น๋๊ธฐ์ ๋ํด์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋ ์ ์ด ์๋ค. ๋ด ๊ธฐ์ต ์ด๋๊ฐ๊ฑฐ์ผ? ๐คฆ
์์ ๊ฐ์ ๊ทธ๋ฆผ์ ์์ ๋ก ๋๊ธฐ์ ๋น๋๊ธฐ๋ฅผ ์ดํดํด๋ณด์!
๋
ธ๋๋ ฅ ์ฐฉ์ทจ์ ํ๊ฐ ๋๋ฒ๋ฆฐ ๋ฉ์ธ ์ฐ๋ ๋๊ฐ Task1
์ ํ์ ๋ณด๋ธ ์ํฉ์ด๋ค.
ํ์ง๋ง ๋ฉ์ธ ์ฐ๋ ๋๊ฐ ํด์ผํ๋ ์์
๋ค(Task2, Task3, Task4)
๊ฐ ์ฌ์ ํ ๋จ์์์ฃ ?
์ด๋ ๋ฉ์ธ ์ฐ๋ ๋๊ฐ ํด์ผํ ํ๋์ผ๋ก ์ฌ๋ฐ๋ฅธ๊ฒ์?
(๋๊ธฐ)
(๋น๋๊ธฐ)
์ด๊ฒ ๋๊ธฐ์ ๋น๋๊ธฐ์ ๊ฐ๋ ์ด๋ค.
์ฌ์ค ์ฒ์ ๊ณต๋ถํ ๋ ๋น๋๊ธฐ์ ๋๊ธฐ ๊ทธ๋ฆฌ๊ณ ๋์์ ์ง๋ ฌ์ ๊ฐ๋ ์ด ๋ค์ฃฝ๋ฐ์ฃฝ ์์ด๋ฉด์ ์์ ๊ฐ๋ ์ด ๋ช ํํ๊ฒ ํ๋ฆฝ๋์ง ์์๋๋ฐ ์ด์ ์ ์ฐ๋ฆฌ๊ฐ ๊ธฐ์ตํ๋ ์ง์๋ค์ ๋ฐฐ์ ํ๊ณ ๋ฑ ์ ๋ ๊ฒ ์ดํดํ์!
์ง๋ ฌ๊ณผ ๋์๋ ํ์ ํน์ฑ์ ๊ดํ๊ฒ์ ์๋ฏธํ๋ค. ์์ iOS์์ ์ ๊ณตํ๋ ํ๋ ํฌ๊ฒ GCD, Operation Queue
2๊ฐ๋ก ๋๋๋ค๊ณ ํ๋๋ฐ 2๊ฐ์ ํ ๋ชจ๋ Serial, Concurrent๋ก ์ฌ์ฉํ ์ ์๋ค.
Serial Queue
์ ์ฌ๋ฌ ์์
๋ค์ ๋ณด๋ด๊ฒ ๋๋ฉด ํด๋น ํ๋ ์์
๋ค์ ํ๋์ ์ฐ๋ ๋์์ ์ฒ๋ฆฌํ๋ค
Concurrent Queue
์ ์ฌ๋ฌ ์์
๋ค์ ๋ณด๋ด๊ฒ ๋๋ฉด ํด๋น ํ๋ ์์
๋ค์ ์ฌ๋ฌ๊ฐ์ ์ฐ๋ ๋์์ ์ฒ๋ฆฌํ๋ค
์ฌ๊ธฐ์ ์๋ฌธ์ด ๋ค ์ ์๋ค
Serial Queue์ Task1,2,3๋ฅผ async ํ๊ฒ ๋ณด๋๋ค.
asyncํ๊ธฐ ๋๋ฌธ์ ๋ฉ์ธ ์ฐ๋ ๋๋ Task4๋ฅผ ๋ฐ๋ก ์คํํ๊ฒ ๋ ๊ฒ์ด๊ณ , Serial Queue๋ ์์
๋ค์ ํ๋์ ์ฐ๋ ๋์์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ํ๋ญ์ด ์ฐ๋ ๋๊ฐ ํ์ ์๋ ๋ชจ๋ ์์
๋ค์ ์์ฐจ์ ์ผ๋ก ์คํํ๊ฒ ๋๋ค.
Concurrent Queue์ Task 1,2,3๋ฅผ syncํ๊ฒ ๋ณด๋๋ค. Concurrent Queue์ ์ด๊ธฐ ๋๋ฌธ์ ํ์ ๋ค์ด์จ ์์ ๋ค์ ๋์์ ์ผ๋ก ์ฌ๋ฌ ์ฐ๋ ๋์์ ์ฒ๋ฆฌ๋๊ณ sync ์ด๊ธฐ ๋๋ฌธ์ ํ์ ๋ค์ด๊ฐ๋ ์์ ๋ค์ด ๋ชจ๋ ์๋ฃ๋์ด์ผ task4๊ฐ ์คํ๋๋ค
ํญ์ ํท๊ฐ๋ ธ๋ ๊ฐ๋
์ ๋ค์ ํ๋ฒ ์ ํํ๊ฒ ์ง์ด ๋ณผ ์ ์๋ ์๊ฐ์ด์๋ค.
Serial/Concurrent ์ Sync/Async ๋ ๋ค๋ฅธ ๊ฐ๋
์ด๋ค!!
ํด๋น ๋ด์ฉ์ ์จ๋ฐ๋์ ๊ฐ์๋ฅผ ๋ฃ๊ณ ์ดํดํ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ธ ์ ๋๋ค.
๊ณ์ํด์ ์ ๋ฆฌ ํ ์์ ์ด๊ณ ํ๋ฆฌ๊ฑฐ๋ ๋ถ์กฑํ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ธ์ ๋ ์ง ์๋ ค์ฃผ์ธ์!
์คํ ๋ฆฌ๋ณด๋์ ๋ทฐ์ปจํธ๋กค๋ฌ์ Custom View์ xibํ์ผ์ด ์ปดํ์ผ ์ดํ์ nibํ์ผ๋ก ๋ณ๊ฒฝ๋๋ ๊ฒ์ ํ์ธํ๋๋ฐ xib์ nib์ ๋ฌด์์ด ๋ค๋ฅธ๊ฒ์ผ๊น?
https://dalgonakit.tistory.com/82
๋ ํ์ผ์ด ์๊ธด ํํ๋ฅผ ๋ณด๋ฉด ํ์คํ๊ฒ ์ฐจ์ด๋ฅผ ์ ์ ์๋ค. Nib์ ๋ฐ์ด๋๋ฆฌ ํํ๋ก Xib๋ XML ํํ๋ก Interface Builder๋ฅผ ์ ์ฅํ๊ณ ์๋ค.
์ด์ ๋ ๋ฒ์ ๊ด๋ฆฌ์ ์๋ค. ๋ฌผ๋ก ์คํ ๋ฆฌ๋ณด๋๋ Xib๋ฅผ ์ฌ์ฉํ๋ ์์ ์์ ๋ฒ์ ๊ด๋ฆฌ๊ฐ ๋ถํธํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ์ฝ๊ณ ์ ํ ์ดํดํ๊ธฐ ํ๋ ๋ฐ์ด๋๋ฆฌ ํํ์ ์์ค์ฝ๋ ๋ณด๋ค๋ xml ํํ๋ก ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ํ๋๊ฒ ๋ ํธํ๊ธฐ ๋๋ฌธ์ด๋ค.
(๊ทธ๋ฆฌ๊ณ Xcode์์๋ Xibํ์ผ์ xml์ด ์๋ ๊ทธ๋ํฝ ํํ๋ก ์์ ํ ์ ์๋๋ก ์ง์ํ๊ณ ์๋ค)
ECDSA(Elliptic Curve Digital Signature Algorithm)๋ ๋น๋์นญํค ์ํธํ์ ํ ์ ํ์ผ๋ก, ํ์ ๊ณก์ ์ํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ๋์งํธ ์๋ช ์๊ณ ๋ฆฌ์ฆ์ ๋๋ค.
ECDSA ์๊ณ ๋ฆฌ์ฆ์ด ์ด๋ค ๋ฐฉ์์ผ๋ก ๊ตฌํ๋์ด์ ์ํธํ๋ฅผ ํ๋์ง์ ๋ํด์๋ ์ ํํ ๋ชจ๋ฅธ๋ค. ๋ชฉํ๋ ECDSA๋ฅผ Swift๋ฅผ ํตํด์ ๊ตฌํํด์ ์ด์ฉํ๋๋ฐ ์ค์ ์ ๋๊ณ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค
ECDSA ์ค๋ช ์์ ๋ค๋ฅธ ๋ถ๋ถ์ ์ ํํ ๋ชจ๋ฅด๋๋ผ๋ 2๊ฐ์ ํค์๋๋ฅผ ํตํด์ ์ด๋ค ๋ฉ์๋๋ค์ด ํ์ํ์ง ์ ์ถํ ์ ์๋ค.
๋์นญํค์ ๋น๋์นญํค์ ๋ํด ์ ๋ฆฌํ๋ฉด์ ๋ค๋ค๋ ๋ถ๋ถ๋ค์
๋๋ค
์์ฐจ์ ์ผ๋ก ํ๋์ฉ ๊ตฌํํด๋ณด๋ฉด์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํด๋ณด์
ECDSA๊ฐ ์ด๋ค ์๊ณ ๋ฆฌ์ฆ์ธ์ง ๋ชจ๋ฅด๋๋ฐ ์ด๋ป๊ฒ ๋ง๋๋์?
์์ฃผ ๋คํํ๋ ์ ํ์ ์ํธํ๋ฅผ ์ํด CryptoKit ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํด์ฃผ๊ณ ์๊ณ ์ฌ์ฉํ๋ ์คํ์ ๋ง๊ฒ P256, P348, P521, Curve25519๋ฅผ ์ ํํ๋ฉด ๋๋ค.
(๊ฐ ์ซ์๊ฐ ์๋ฏธํ๋๊ฑด Key Size๋ก ์ด๋ฏธ ๊ตฌํ๋๊ณณ๊ณผ ๋ง์ถฐ์ค์ผ ํ๋ค. ECDSA256์ P256์ ์ฌ์ฉํ๋ฉด ๋๋ค)
import CryptoKit
import Foundation
final class ECDSA {
private let privateKey: P256.Signing.PrivateKey
private let publicKey: P256.Signing.PublicKey
// MARK: - Generate ECDSA Key Pair
init() {
self.privateKey = .init()
self.publicKey = privateKey.publicKey
}
func getPrivateKey() -> String {
privateKey.derRepresentation.base64EncodedString()
}
func getPublicKey() -> String {
publicKey.derRepresentation.base64EncodedString()
}
}
์ฝ๋์์ ํ์ธํด์ผ ๋ ๋ถ๋ถ์
์ฒซ๋ฒ์งธ๋ ์ฝ๋๋ฅผ ํตํด์ ๋ฐ๋ก ํ์ธํ ์ ์์ง๋ง ๋๋ฒ์งธ representation์ ์ข ๋ฅ๋ ์๋ฒ๋ ์ด๋ฏธ ๊ตฌํ๋ ๊ณณ๊ณผ ํํ๋ฅผ ๋ง์ถฐ์ ์ฌ์ฉํด์ผ ํ๋ค.
์ฒ์ ๊ตฌํํ ๋ ์ด๋ถ๋ถ์์ ์ ๋ฅผ ์ข ๋จน์๋๋ฐ ๊ฐ๋จํ๊ฒ ์ข ๋ฅ์ ํน์ฑ์ ํ์ธํด๋ณด์
/// Creates a P-256 private key for signing from a collection of bytes.
///
/// - Parameters:
/// - rawRepresentation: A raw representation of the key as a collection of
/// contiguous bytes.
public init<Bytes>(rawRepresentation: Bytes) throws where Bytes : ContiguousBytes
/// Creates a P-256 private key for signing from a Privacy-Enhanced Mail
/// PEM) representation.
///
/// - Parameters:
/// - pemRepresentation: A PEM representation of the key.
@available(iOS 14.0, macOS 11.0, watchOS 7.0, tvOS 14.0, *)
public init(pemRepresentation: String) throws
/// Creates a P-256 private key for signing from a Distinguished Encoding
/// Rules (DER) encoded representation.
///
/// - Parameters:
/// - derRepresentation: A DER-encoded representation of the key.
@available(iOS 14.0, macOS 11.0, watchOS 7.0, tvOS 14.0, *)
public init<Bytes>(derRepresentation: Bytes) throws where Bytes : RandomAccessCollection, Bytes.Element == UInt8
/// - Parameters:
/// - x963Representation: An ANSI x9.63 representation of the key.
public init<Bytes>(x963Representation: Bytes) throws where Bytes : ContiguousBytes
์ฐ์ ์ข ๋ฅ๋ง ๋ณด๋ฉด 4๊ฐ์ง์ Representation์ด ์กด์ฌํ๊ณ pemRepresentation์ ์ ์ธํ๊ณ ๋๋จธ์ง๋ Data Type์์ ์ ์ ์๋ค.
์๋ฏธ ๊ทธ๋๋ก ๋ณ๋ค๋ฅธ ๊ท์น์์ด ๋จ์ํ Data Type์ผ๋ก ์์ฑํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
์๋ฒ์์ ๋ค๋ฅธ Representation์ ์ฌ์ฉํ๊ณ ์๋๋ผ๋ raw์์ ๋ณํ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ์์ Private Key๋ฅผ ์์ฑํ๊ณ ์ ์ฅํ ๋๋ rawRepresentation ์ ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๋ค
pem์ ํ ์คํธ ๊ธฐ๋ฐ์ ์ํธํ ํ์์ผ๋ก Base64๋ก ์ธ์ฝ๋ฉ๋ ๋ฐ์ดํฐ๋ฅผ ์์๊ณผ ๋์ ํน์ ํ ํ๊ทธ๋ฅผ ๋ถ์ฌ ํํํ๋ค.
pem ํ์์ผ๋ก public key๋ฅผ ์์ฑํ๋ฉด String Type์ผ๋ก ํค๊ฐ ์์ฑ๋๊ณ ํค์ ์ฒ์๊ณผ ๋์ - - -Private Key - - -์ ๊ฐ์ ํ๊ทธ๊ฐ ์์ฑ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
Creates a P-256 private key for signing from a Distinguished Encoding
ํน์ ํ ํ๊ทธ ๋ฐ ๊ธธ์ด์ ๋ณด๋ฅผ ํฌํจํด์ ๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํ ํด์ ์ ์ฅํ๋ค (์ฌ์ค ์ ํํ ์๋ฏธ๋ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค)
์ฌ์ค derRepresentation๊ณผ x963Representation์ด ์ด๋ค ์๋ฏธ๋ฅผ ๊ฐ์ง๋์ง๋ ๋ชจ๋ฅด์ง๋ง Data Type์ base64String ์ด๋ hexString์ผ๋ก ๋ณํํด์ ์ด๋ฏธ ๊ตฌํ๋ ๋ถ๋ถ๊ณผ ๊ธธ์ด๋ฅผ ๋น๊ตํ๋ฉด ์ด๋ค Representation์ ์ฌ์ฉํ๊ณ ์๋์ง ํ์ธํด ๋ณผ ์ ์๋ค.
ECDSA ํค ์์ฑ ๋ฐ ์ ์ฅ์ ๋ํด์ ์ ๋ฆฌํด๋ณด๋ฉด,
์์คํ
์ฝ
์ด๋ ํ๋ก์ธ์ค๊ฐ ์ปดํจํฐ๋ฅผ ๊ด๋ฆฌํ๋ ์์คํ
์ธ ์ด์์ฒด์ (OS)์๊ฒ ์ด๋ค ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ฒ ํด๋ฌ๋ผ๊ณ ์์ฒญํ ๋ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์
๋๋ค.์ด์ ์ ํ๋ก์ธ์ค๋ ์ด์์ฒด์ ๋ก ๋ถํฐ ์์์ ํ ๋น๋ฐ๋ ์์ ์ ๋จ์๋ผ๊ณ ๊ณต๋ถ ํ๋๋ฐ ๋ง์ฝ ํ๋ก์ธ์ค๊ฐ ์๊ธฐ ํ๋ก๊ทธ๋จ ์ด์ธ์ ํน์ ํ์ผ ๋ฐ์ดํฐ๋ฅผ ํ์๋ก ํ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ํ ๊น์?
ํ๋ก์ธ์ค๋ ๋ค๋ฅธ ํ๋ก์ธ์ค์ ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ ์ด์์ฒด์ ์ ์์ฒญ์ ํตํด์ ํด๋น ๋ฐ์ดํฐ์ ์ ๊ทผํ๊ฒ ๋๊ณ ์ด๋ฐ ์์ฒญ์ ์์คํ
์ฝ
์ด๋ผ๊ณ ํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ด์์ฒด์ ๋ User Mode
์ Kernel Mode
๋ก ๋
๋ฆฝ๋ ๋์ ๋ชจ๋๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
์์ ๋งํ ์ํฉ์ฒ๋ผ, ํ๋ก๊ทธ๋จ์ด ๊ตฌ๋๋๋๋ฐ ํ์ผ์ ์ฝ์ด ์ค๊ฑฐ๋, ์ฐ๊ฑฐ๋, ์ถ๋ ฅํ๋ ๋ถ๋ถ์ Kernel Mode
๋ฅผ ํตํด์ ๊ฐ๋ฅํฉ๋๋.
์ผ๋ฐ์ ์ผ๋ก ๊ฐ์ธ ํ๋ก์ ํธ ์์ค์์ ์๋ฒ๊ฐ ํ๊ฒฝ๋ณ๋ก ๋ถ๋ฆฌ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐํ๊ฒฝ์ ๋ฐ๋ก ๋๋ ์ ์์ ์ ํด๋ณธ ๊ฒฝํ์ด ์์๋ค. (๋ฌผ๋ก ๊ด๊ณ ๋ฅผ ๋ถ์ด๋ ๊ฒฝ์ฐ์๋ ํ์ํ ๊ฒ ๊ฐ๋ค)
ํ์ง๋ง ์ผ๋ฐ์ ์ธ ํ์ฌ ํ๋ก์ ํธ๋ฅผ ์งํํ๊ฒ ๋๋ฉด ๊ฐ๋ฐํ๊ฒฝ์ ๋๋ ํ์์ฑ์ด ์๊ธฐ๊ฒ ๋๋ค.
ํ์ฌ๋ง๋ค ๋ค๋ฅด๊ฒ ์ง๋ง ์๋ฒ์ DB๊ฐ dev, stage, production
์ ๋๋ก ๋๋์ด์ ธ ์์ ๊ฒ์ด๋ค. ๊ทธ์ ๋ฐ๋ผ์ ๋น์ฐํ ํ๊ฒฝ๋ณ๋ก ์๋ฒ๋ช
, ๋๋ฉ์ธ, IP, ์ ์๊ณ์ ๋ฑ ํ๋กํผํฐ ๊ฐ์ด ๋ค๋ฅด๋ค.
๋ฐฐํฌ ๊ด์ ์์๋ ์ผ๋ฐ์ ์ผ๋ก dev๋ฅผ ํตํด ๊ฐ๋ฐ์ ์งํํ๊ณ stage๋ก qa์์ ์ ์งํํ๊ณ ์ต์ข ์ ์ผ๋ก prod๋ก ๋ฐฐํฌ๋ฅผ ์งํํ๊ฒ ๋๋๋ฐ qa์์ ๋ฐ๊ฒฌ๋ ๋ฒ๊ทธ๋ค์ ์์ ํ๋ฉด์ ๋์ผ ๋ฒ์ ๋ด์์ ๋ค๋ฅธ ๋น๋๋ฒํธ๊ฐ ์์ ๋ค์ด testflight์ ์์ด๊ฒ ๋๋ค.
์ด๋ ํ๊ฒฝ๋ถ๋ฆฌ ์์ด ์์ ํ๋ค๊ฐ ์๋ฌ๊ฐ ์๋ ๋ฒ์ ์ ๋ฐฐํฌํ๊ฒ ๋๋ ๋ ์๋ ์์ํ๊ณ ์ถ์ง ์๋ค.
์ด๋ฐ ๋ฌธ์ ์ ๋ค์ ๊ฐ๋ฐํ๊ฒฝ์ ๋๋ ์ ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ url์ ๊ฐ์ง๋๋ก ์ค์ ํ๊ณ , ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ name, bundle identifier๋ฅผ ์ค์ ํจ์ผ๋ก์ ํด๊ฒฐํ ์ ์๋ค.
๋ฌผ๋ก Target์ ํตํด์ ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ product๊ฐ ์์ฑ๋๋๋ก ์ค์ ํด ๊ฐ๋ฐํ๊ฒฝ์ ๋๋์๋ ์์ง๋ง ์ผ๋ฐ์ ์ผ๋ก ๋ฌด๋ฃ/์ ๋ฃ ๋ฒ์ ๊ตฌ๋ถ, ์์ ฏ๋ฑ ๊ฐ์ ์ฑ์ด์ง๋ง ๋ค๋ฅธ ์ฌ์ฉ์ฑ์ ์ ๊ณตํ ๋ ์ฌ์ฉํ๋ค.
(๊ด๋ จ ๊ณต์ ๋ฌธ์๋ฅผ ์๊ณ ๊ณ์๋ฉด ์ฐ๋ฝ ๋ถํ๋๋ฆฝ๋๋ค!!)
๋ฐ๋ผ์ ํ๋์ Target์ Produect๋ฅผ Build Configuration์ ํตํด ๊ฐ๋ฐํ๊ฒฝ์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค.
Build Configuaration๋ ๋น๋ํ ๋ ํ๊ฒฝ์ ์ ์ดํ๋ ๊ฐ ์ ๋๋ก ์๊ฐํ๋ฉด ๋๋ค.
ํ๋ก์ ํธ์์ ๊ธฐ๋ณธ์ ์ผ๋ก Debug, Release๊ฐ ์ค์ ๋์ด ์๊ณ , ์คํด์์ ๋น๋์ ๋ฐฐํฌ์ ๋ฐ๋ผ ๋ค๋ฅธ configuartion์ ์ฌ์ฉํ๊ณ ์๋๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทธ๋ผ configuration์ ์ด์ฉํด์ ๊ฐ๋ฐํ๊ฒฝ์ ๋๋ ๋ณด์.
ํฌ๊ฒ ์ธ ๊ฐ์ง ๊ณผ์ ์ผ๋ก ๋๋์ด์ ์ ๋ฆฌํ ์์ ์ด๊ณ ํน์๋ ๋น ์ง๋ถ๋ถ ์๋ค๋ฉด ๊น์ข ๊ถ๋์ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ๋๋ค.
๊ธฐ์กด์ Debug, Release๋ฅผ ๋ณต์ฌํด์ dev, stage, prod ๋ฅผ ๋ง๋ ๋ค. (dev release๋ ์ํฉ์ ๋ง๊ฒ ๋ง๋ ๋ค.)
๊ทธ๋ฆฌ๊ณ dev, stage, prod์ ํด๋นํ๋ ์คํด์ ์์ฑํ๋ค. ์คํด์ ์์ฑํ๋ ์ด์ ๋ ํ๊ฒฝ๋ณ๋ก edit ์คํด์ ํตํด์ build configuration์ ์์ ํ๋ ๊ณผ์ ์ด ๋ฒ๊ฑฐ๋กญ๊ณ ์ค์๋ฅผ ์ค์ด๊ธฐ ์ํด์ ๋ฏธ๋ฆฌ ๊ฐ ํ๊ฒฝ์ ๋ง๋ ์คํด์ ์์ฑํ๋ค.
(์ฒ์์๋ ์คํด์ ํตํด์ ๊ฐ๋ฐํ๊ฒฝ์ ๋๋๋ค๊ณ ์๊ฐํ์๋๋ฐ ์คํด์ ๋น๋ํ ๋ ์ด๋ค build configuration ์ด์ฉํ ์ง ์ ํํ๋ ๊ฒ์ด์ง ์คํด์ ํตํด ๊ฐ๋ฐํ๊ฒฝ์ ๋๋๋ค๋ ํํ์ ๋ฐ์ชฝ ์๋ฆฌ ์ ๋ต์ธ๊ฒ ๊ฐ๋ค.)
์ด์ ๊ฐ ํ๊ฒฝ๋ณ๋ก xconfig ํ์ผ์ ์์ฑํ๊ณ ํ๊ฒฝ์ ๋ง๊ฒ build configuration์์ xconfig ํ์ผ์ ์ค์ ํด์ค๋ค.
์ฌ๊ธฐ๊น์ง ์์ ์ ๋ง์น๋ฉด ์ด์ ๋ชจ๋ ์ธํ ์ด ๋๋ฌ๊ณ xconfig ํ์ผ์ ํตํด์ ํ๊ฒฝ๋ณ๋ก ๊ฐ์ ์ธํ ํ๋ ๊ณผ์ ๋ง ๋จ์๋ค.
// Prod.xconfig
PRODUCT_BUNDLE_IDENTIFIER = com.bran.envsetting.app
PRODUCT_NAME = EnvSetting
DEPLOY_PHASE = prod
// Stage.xconfig
PRODUCT_BUNDLE_IDENTIFIER = com.bran.envsetting.stage.app
PRODUCT_NAME = EnvSetting-stage
DEPLOY_PHASE = stage
// Dev.xconfig
PRODUCT_BUNDLE_IDENTIFIER = com.bran.envsetting.dev.app
PRODUCT_NAME = EnvSetting-dev
DEPLOY_PHASE = dev
ํ๊ฒฝ๋ณ .xconfig ํ์ผ์์ ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ ์ฑ์ ๋น๋ํ ์ ์๋๋ก ๊ณ ์ ํ identifier๊ฐ๋ค์ ์ค์ ํด์ค๋ค.
์์์ ์ฐ๋ฆฌ๊ฐ ํ๊ฒฝ์ ๋ง๊ฒ build configuration์ xconfig ํ์ผ์ ๊ฐ์ ๋ฐ๋ฅด๋๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์ ์ฌ๊ธฐ๊น์ง๋ง ์์ ์ ํด๋ ํ๋ก์ ํธ ๋น๋ ์ธํ ์์ ๊ฐ์ด ๋ณ๊ฒฝ๋์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด์ ๋ ๋จ์ํ๋ค. ํ๊ฒ์ ์ค์ ๊ฐ๋ค์ ๋ฐ๋์ง ์์์ฑ๋ก ์ฌ์ ํ ๋จ์์๊ธฐ ๋๋ฌธ์ด๋ค.
ํ๊ฒ์ ๋น๋ ์ค์ ๊ฐ๋ค์ ํ๋ก์ ํธ์ ๊ฐ๋ค์ ์์
๋ฐ๊ณ ํ๊ฒ์ product์ ๋น๋ ์ค์ ๊ฐ์ ํ๊ฒ์ ์ค์ ๊ฐ์ ๋ฐ๋ฅธ๋ค.
๋ฐ๋ผ์ ํ๊ฒ์ ์ค์ ์์ ์ฐ๋ฆฌ๊ฐ ์ค์ ํ ํ๊ฒฝ๋ณ์ ๊ฐ์ ์ฌ์ฉํ๋๋ก ์ค์ ํ๋ ๊ณผ์ ์ด ํ์ํ๋ค. ์ด์ ๋ค์ ์คํด๋ณ๋ก ํ ์คํธํ๋ฉด ํ๊ฒฝ๋ณ๋ก ์๋ก ๋ค๋ฅธ ์ฑ 3๊ฐ๊ฐ ์์ฑ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
Bundle Identifier๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ TestFlight์์ Stage, Prod
๋ฅผ ํผ๋ํด ์ค์ํ ์ผ์ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์์ด์ง๋ค. ํ์ง๋ง QA์์
์ ํ๋ค๋ณด๋ฉด Stage, Prod์ ๋ฒ์ ์ด๋ ๋น๋๋๋ฒ๊ฐ ์์ดํด์ง๋๋ฐ ์ด๋ฅผ ์ด๋ป๊ฒ ๊ด๋ฆฌํด์ผ ํ ๊น?
๋ฌผ๋ก , ๋ธ๋์น ์ ๋ต์ ํตํด์ ๊ด๋ฆฌํ ์๋ ์์ง๋ง xcode์์ ๋น๋๋๋ฒ, ๋ฒ์ ์ .pbx ํ์ผ์ ํตํด์ ๊ด๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ค์ํ๊ธฐ ์ฝ๋ค.
์์ธ๋ก ๊ฐ๋จํ๋ฐ ์ฐ๋ฆฌ๊ฐ ์ค์ ํ configuration ๋ณ๋ก ๋ฒ์ , ๋น๋๋๋ฒ๋ฅผ ๋ค ๋ฐ๋ก ์ค์ ํ ์ ์๋ค.
info.plist์ ํ๊ฒฝ๋ณ๋ก ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฐ์ xconfig์์ ์ค์ ํ๊ณ ์ฑ์ด ์์ํ๋ ์์ ์์ info.plist ๊ฐ์ ๋ถ๋ฌ์ค๋ฉด ๋๋ค.
enum DeployPhase: String {
case prod
case stage
case dev
}
final class Configuration {
private init() { }
private static let configKey = "DeployPhase"
// MARK: - Get Current DeployPhase
static func getDeployPhase() -> DeployPhase {
guard
let configValue = Bundle.main.object(forInfoDictionaryKey: configKey) as? String,
let phase = DeployPhase(rawValue: configValue)
else {
return DeployPhase.dev
}
return phase
}
}
import SwiftUI
@main
struct EnvSettingApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear {
print(Configuration.getDeployPhase())
}
}
}
}
๋ฑ๋ฑ ๋ค์ํ ๋ฌธ์ ๋ค์ ๊ฒช๊ณ ๋๋ฆ๋๋ก ํด๊ฒฐํ ๋ฐฉ๋ฒ์ ์ถํ์ ํ๋์ฉ ์ ๋ฆฌํ ์์ ์ด๋ค.
์ฐ๋ฆฌ๊ฐ ์ค์ ํ ํ๊ฒฝ๋ณ๋ก ๋ถ๊ธฐ์ฒ๋ฆฌ๋ฅผ ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
(plist ์ฌ์ฉ)
๋ฐฉ๋ฒ์ ๋ง์ง๋ง ์์ ๋ ๋ฐฉ๋ฒ์ ํตํด์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค. ์ฒซ๋ฒ์จฐ ๋ฐฉ๋ฒ์ ์ง๋๋ฒ์ ์ ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๋๋ฒ์จฐ ๋ฐฉ๋ฒ์ ๋ํด์ ์ ๋ฆฌํด๋ณด์.
์ ์ฒ๋ฆฌ๋ฌธ์ ์ปดํ์ผ ์ด์ ์ ์ฒ๋ฆฌ๋๋ ๋ฌธ์ฅ์ผ๋ก ์ฃผ๋ก C์ธ์ด์์ #defin์ผ๋ก ์์ฃผ ์ฌ์ฉํ์๋๋ฐ iOS์์๋ ์ด๋ป๊ฒ ์ฌ์ฉํ ๊น?
// MARK: - OS ๋ถ๊ธฐ์ฒ๋ฆฌ
#if os(iOS)
print("iOS")
#elseif os(macOS)
print("macOS")
#else
print("other OS")
#endif
// MARK: - Flag ๋ถ๊ธฐ์ฒ๋ฆฌ
#if DEBUG
print("Debug")
#else
print("Relase")
#endif
(์ ์ฒ๋ฆฌ๋ฌธ์ด ๋ญ์ผ? ํ๋ ๋ถ๋ค๋ ์๋ง ์์ ์ฝ๋๋ ํ๋ฒ์ฉ์ ๋ดค๋ ๊ธฐ์ต์ด ์์๊ฒ๋๋ค)
Swift์๋ ์ ์ฒ๋ฆฌ๊ธฐ๊ฐ ์๋ค๋ ์ฌ์ค์ ์๊ณ ๊ณ์
จ๋์?? -> ํด๋น ๋ด์ฉ์ ๋น๋๊ณผ์ ์ ์ ๋ฆฌํ๋ฉด์ ํ๋ฒ ์ ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
์ ๋ชฉ์ ํตํด์๋ ์ ์ ์๊ฒ ์ง๋ง ์ฐ๋ฆฌ๋ Flag๋ฅผ ์ด์ฉํด์ ๊ฐ๋ฐํ๊ฒฝ๋ณ๋ก ์ ์ฒ๋ฆฌ๋ฌธ์ ์ด์ฉํ ์์ ์ด๋ค.
ํ๋ก์ ํธ๋ฅผ ์ฒ์ ์์ฑํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก DEBUG
Flag๊ฐ ์กด์ฌํ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
์ธํ ์์ ๋ฐ๋ก ๊ฐ์ ์์ ํด๋ ๋์ง๋ง Build Configuration์ ํ๊ฒฝ๋ณ์๋ค์ .xconfig ํ์ผ์์ ์ ๋ฆฌํ๊ณ ์ค์ ํ๋ ๋ฒ์ ์ง๋๋ฒ์ ์ ๋ฆฌ ํ๊ธฐ ๋๋ฌธ์ ํด๋น ๋ฐฉ๋ฒ์ ์ ์ฉํด๋ณด์.
// MARK: - Prod.xconfig
PRODUCT_BUNDLE_IDENTIFIER = com.bran.envsetting.app
PRODUCT_NAME = EnvSetting
CUSTOM_FLAG = PROD
DEPLOY_PHASE = prod
// MARK: - Stage.xconfig
PRODUCT_BUNDLE_IDENTIFIER = com.bran.envsetting.stage.app
PRODUCT_NAME = EnvSetting-stage
CUSTOM_FLAG = STAGE
DEPLOY_PHASE = stage
// MARK: - Dev.xconfig
PRODUCT_BUNDLE_IDENTIFIER = com.bran.envsetting.dev.app
PRODUCT_NAME = EnvSetting-dev
CUSTOM_FLAG = DEV
DEPLOY_PHASE = dev
๋ฐฉ๋ฒ ์์ฒด๋ ์ง๋๋ฒ bundle identifier, app name ์ ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ ์์ ํ ๋์ผํ๊ธฐ ๋๋ฌธ์ ์๋ต
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
#if DEV
Text("Hello, DEV world!")
#elseif STAGE
Text("Hello, STAGE world!")
#else
Text("Hello, world!")
#endif
}
.padding()
}
}
firebase, swiftgen๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค๊ตญ์ด ํ์ผ ๊ฐ์ ๊ฒฝ์ฐ Build Phase์์ run script๋ฅผ ํตํด ํ๋ก์ ํธ๋ก ๋น๋ํ๋ ๊ณผ์ ์ค์ ์์ค ํ์ผ๋ค์ ์์ฑํ๋ค.
case "${CONFIGURATION}" in
"Debug(Dev)" | "Release(Dev)" )
echo "DEV" ;;
"Debug(Stage)" | "Release(Stage)" )
echo "STAGE" ;;
"Debug" | "Release" )
echo "PROD" ;;
*)
;;
esac
์์ ๊ฐ์ด ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ๊ณ build log์์ ํ๊ฒฝ๋ณ๋ก ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ์ด ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
enum State {
case reday
case stop
case go
}
let state: State = .go
if state == .go {
print("True")
}
์ผ๋ฐ์ ์ผ๋ก ์ด๊ฑฐํ์ ์ฌ์ฉํ ๋ ์์ ๊ฐ์ด case๋ฅผ ์ง์ ๋น๊ตํด์ ์ฌ์ฉํ ์ ์์์ต๋๋ค. ์ฐ๊ด๊ฐ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ๋ ๊น์?
enum States: Equatable {
case ready(String)
case stop(Int)
case go
}
let states: States = .ready("Ready")
if states == .ready("Ready") {
print("True")
} else {
print("False")
}
์ฐ๊ด๊ฐ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์์ ๊ฐ์ case ๋น๊ต๊ฐ ๋ถ๊ฐ๋ฅํด์ง๋๋ค. Equtable ํ๋กํ ์ฝ์ ์ฑํํ๋ฉด ๊ฐ๋ฅํ๊ธด ํฉ๋๋ค.
if case let .ready("Ready") = states {
print("True")
}
์ด๊ฑฐํ
๊ทธ๋ฆฌ๊ณ ์ ๋ค๋ฆญ(Wrapped)์ ์ฌ์ฉํ๊ณ ์๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.var optionalValue: Optional<Int> = nil
if optionalValue == .none {
print("This Value is nil")
}
if optionalValue == nil {
print("This Value is nil")
}
์ํ์ด ์ด๊ฑฐํ์ด๊ธฐ ๋๋ฌธ์ ๋น์ฐํ ์์ ๊ฐ์ด ํ์ธ ํด ๋ณผ ์ ์๋ค.
ExpressibleByNilLiteral
ํ๋กํ ์ฝ์ ์๋ฏธ๋ฅผ ํ์ธํด๋ณด์ExpressibleByNilLiteral
ํ๋กํ ์ฝ์ ๋ณ์๋ฅผ ์ ์ธํ ๋ nil๋ก ์ด๊ธฐํ ํ ์ ์์์ ์๋ฏธํ๋ค
์์ ๋ฌธ์ฅ์ด ์ดํด๊ฐ ๋์ง ์๋๋ค๋ฉด ์ฐ๋ฆฌ๊ฐ ํ์
์ถ๋ก ์ ํตํด์ ๋ณ์๋ ์์๋ฅผ ์ด๊ธฐํ ํ๋ ๊ฒ์ ์๊ฐํด๋ณด๋ฉด ๋๋ค.
var name = "Sangwon" // ์ด๋ป๊ฒ ๋ฌธ์์ด ๋ฆฌํฐ๋ด์ ํตํด์ ํ์
์ถ๋ก ์ด ๊ฐ๋ฅํ์๊น?
์ด๋ String์ด ExpressibleByStringLiteral
ํ๋กํ ์ฝ์ด ์ฑํํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ๋ค.
public init(_ some: Wrapped) // Optional Value
public init(nilLiteral: ()) // nil Value
๊ทธ๋ฆฌ๊ณ ์์ฑ์๋ฅผ ํ์ธํด๋ณด๋ฉด ์์ ๊ฐ์ด 2๊ฐ์ ์์ฑ์๋ฅผ ๊ฐ์ง๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
Swift Source Code ๋ณด์ถฉํ๊ธฐ!
(๊ฒฝํ์ ๋น์ถ์ด ๋ณด๋ฉด),
์ผ๋ฐ์ ์ผ๋ก GET์ ํตํด์ API ํต์ ์ ํ๋ฉด ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ํตํด ํํฐ๋ง ์ต์ ์ ๋ฃ์ด์ฃผ๋ฉด ์ต์ข ์ ์ธ URI ํํ๊ฐ ์ฐ๋ฆฌ๊ฐ ๋ฃ์ด์ค ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๊ฐ {key - value} ์์ผ๋ก ๋ค์ด๊ฐ๋ค. (HTTP ๋ฒ์ ์ ๋ฐ๋ผ ๋ฉ์์ง ๋ฐ๋ ์ฌ์ฉ ๊ฐ๋ฅ)
POST, PUT, PATCH ๊ฐ์ ๋ฉ์๋๋ค์ ํด๋ผ์ด์ธํธ์์ ์๋ฒ์ ๋ณด๋ด๋ ค๋ ๋ฐ์ดํฐ๋ฅผ json์ผ๋ก ์ธ์ฝ๋ฉ ์์ผ์ ๋ฉ์์ง ๋ฐ๋์ ๋ฃ์ด์ ๋ฐ์ดํฐ ์ ์ก์ ํ๋ค.
์น ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ์๋๋ผ์ HTML Form ๋ฐ์ดํฐ ์ ์ก์ ๋ํด์ ์ฒ์ ๋ค์ด๋ดค๋๋ฐ HTML Form์ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์ ์ ์กํ๋ ๋ฐฉ์์ด ๋ฐ๋ก ์๋ค ์ ๋๋ก ์ดํดํ๋ค.
๊ฐ์์์ ํด๋น๋ด์ฉ์ ์ค๋ช ํ๋ฉด์ HTML Form์์ ์ฌ์ฉํ๋ Content-Type์ ๋ํด ์ค๋ช ํ๋ ๋ถ๋ถ์ด ์๋๋ฐ ์ด ๋ถ๋ถ์ ํ๋ฒ ์ง๊ณ ๋์ด๊ฐ๋ณด์
(์ต์ํ์ฃ ..?)
Content-Type์ HTTP ์์ฒญ ๋๋ ์๋ต์ ๋ณธ๋ฌธ(content)์ ๋ฏธ๋์ด ํ์ ์ ๋ํ๋ด๋ ํค๋ ํ๋๋ก ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ๋ก ์ ์กํ๋ ๋ฐ์ดํฐ์ ํ์์ ๋ช ์ํ๊ณ , ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ ๋ฐ์ดํฐ์ ํ์์ ์๋ ค์ค๋๋ค.
Content-Type: media-type
**media-type
**์ MIME(Multipurpose Internet Mail Extensions) ํ์
์ผ๋ก ๋ฐ์ดํฐ ๋ฏธ๋์ด ํ์
์ ๋ช
์ํ๋ ๋ฌธ์์ด
์์ฝํ์๋ฉด
์๋ฒ <-> ํด๋ผ์ด์ธํธ
๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ค.application/json
์ง๋ ์๊ฐ์ ์ ๋ฆฌํ๋ HTTP API ์ค๊ณ์ ์ฐ์ฅ์ ์ด ๋๋ ๋ถ๋ถ์ธ๋ฐ ๋ณต์ตํ๋ ๊ฒธ ๋ค์ ํ๋ฒ ์ ๋ฆฌํด๋ณด์
์ปฌ๋ ์
์คํ ์ด
์ผ๋ฐ์ ์ผ๋ก ์ปฌ๋ ์ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
DB์ ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ API๋ ์ผ๋ฐ์ ์ผ๋ก PATCH, POST๋ฅผ ํตํด์ ์ค๊ณํ๋๋ฐ PUT์ ์ด์ฉํ ์ ์์ง๋ง ๋ถ๋ถ ์์ ์ด ๋ถ๊ฐ๋ฅํ๊ณ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ณด๋ด์ค์ผ ํ๊ธฐ ๋๋ฌธ
ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ๋ก ๋ณด๋ธ ์์ฒญ์ ์ฒ๋ฆฌ ์ํ๋ฅผ ์๋ ค์ฃผ๋ ๊ธฐ๋ฅ์ผ๋ก ์๋์ ๊ฐ์ด 5๊ฐ์ง๋ก ๋ถ๋ฅํด์ ์ฌ์ฉํ๋ค.
(1xx๋ ๊ฑฐ์ ์ฌ์ฉ๋์ง ์๊ธฐ ๋๋ฌธ์ ์๋ต)
ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ๋ก ๋ณด๋ธ ์์ฒญ์ ์ฑ๊ณตํด์ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด์ค ๋์ ์ํ์ฝ๋๋ฅผ ์๋ฏธ
์์ฒญ์ด ์ฒ๋ฆฌ๋์ด์ ์๋ก์ด ๋ฆฌ์์ค๊ฐ ์์ฑ๋์์์ ์๋ฏธ
POST๋ฅผ ํตํด์ ์๋ก์ด ๋ฆฌ์์ค๋ฅผ DB์ ์์ฑํ๋ ๊ฒฝ์ฐ๋ฅผ ๊ฐ์ ํด๋ณด์.
PUT๊ณผ ๋ฌ๋ฆฌ POST๋ฅผ ํตํด ๋ฆฌ์์ค๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ ํด๋ผ์ด์ธํธ์์๋ ๋ฆฌ์์ค์ URI๋ฅผ ๋ชจ๋ฅธ๋ค. ์ด๋ ์ฑ๊ณต์ ์ผ๋ก ๋ฆฌ์์ค๊ฐ ์์ฑ๋๋ฉด ํด๋น ๋ฆฌ์์ค์ URI๋ฅผ ์๋ต์ ํค๋์ (Location)์ ์ค์ด์ ๋ณด๋ด์ค๋ค.
์์ฒญ์ด ์ ์๋์์ผ๋ ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋์ง ์์ ์ํ
์๋ฒ๊ฐ ์์ฒญ์ ์ฑ๊ณต์ ์ผ๋ก ์ํํ์ง๋ง, ํด๋ผ์ด์ธํธ์ ๋ณด๋ผ ๋ฐ์ดํฐ๊ฐ ์์
์ฑ์์ ์ ์ ๊ฐ ๊ฒ์๊ธ์ ์์ ํ๊ณ ์ ์ฅ ๋ฒํผ์ ๋๋ฅธ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด๋ณด์. PUT, POST, PATCH ๋ฉ์๋๋ฅผ ์ด์ฉํด ๊ฒ์๊ธ ์์ ์ ์๋ฒ๊ฐ ์์ฒญ๋ฐ๊ณ ํด๋น ์์ ์ ์๋ฃ ๋์์์ ํํํ ๋ ํด๋ผ์ด์ธํธ์๊ฒ ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ์ค์ผํ ๊น?
200 OK์ ์ฑ๊ณตํ์์ ์๋ฏธํ๋ ํํ์ ๋ด์์ ๋ณด๋ผ ์๋ ์๊ฒ ์ง๋ง ์ฌ์ค ์ฑ๊ณตํ์
๋ผ๋ ์ฌ๋ถ๋ง ํ์ํ ๊ฒ์ด์ง ์ด๋ ํ ๋ฐ์ดํฐ๋ ํ์ํ์ง ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ์ 204 No Content๋ฅผ ์ฌ์ฉํ๋ค.
์ฌ์ค ํด๋ผ์ด์ธํธ ๊ฐ๋ฐ ์ ์ฅ์์๋ ์์ ๊ฐ์ ์ํฉ์์ 200, 204๋ ์๊ด์์ด Response Body๊ฐ์ ๋์ฝ๋ฉํ๋ ๊ณผ์ ์์ด ์ํ์ฝ๋๋ง์ ์ด์ฉํด์ ์ ์ ์๊ฒ ์ฑ๊ณต, ์คํจ ์ฌ๋ถ๋ฅผ ์๋ ค์ฃผ๋ ๋ฐฉ์์ผ๋ก ๊ฐ๋ฐํ๋ฉด ๋ ๊ฒ ๊ฐ์๋ฐ 204๋ผ๋ ์ํ์ฝ๋์ ์๋ฏธ๋ฅผ ํตํด์ ๋ ๋ช ํํ ํ ์ ์๊ฒ ๋ค๋ผ๋ ์๊ฐ์ด ๋ค์๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ์์ฒญ์ ์๋ฃํ๊ธฐ ์ํด์ ์ ์ ์์ด์ ํธ(์ดํ๋ฆฌ์ผ์ด์ )์ ์ถ๊ฐ ์กฐ์น ํ์๋ฅผ ์๋ฏธ
๋จ์ด์๋ฏธ ๊ทธ๋๋ก ๋ค์ ๋ฐฉํฅ์ ๊ฐ๋ฆฌํค๋๊ฒ์ ์๋ฏธํ๋ค.
์น ๋ธ๋ผ์ฐ์ ๋ 3xx ์๋ต์ ๊ฒฐ๊ณผ์ Location ํค๋๊ฐ ์์ผ๋ฉด Location ์์น๋ก ์๋ ์ด๋ํ๋ค.
์ถ์ ์ด๋ฒคํธ ํ์ด์ง๊ฐ ๋ณ๊ฒฝ๋์์ ๋๋ฅผ ์๊ฐํด๋ณด์. ์ ์ ๊ฐ ๋ง์ฝ ๋ถ๋งํฌ๋ ์ด์ ์ ์ด๋ฒคํธ ํ์ด์ง์ ๋ค์ด๊ฐ์ ๊ฒฝ์ฐ ์๋ฒ์์ 300๋ฒ๋ ์๋์ฝ๋์ ์๋ก์ด ์ด๋ฒคํธ ํ์ด์ง๋ฅผ ๋ณด๋ด์ฃผ๊ณ ์ ์ ์ URL์ ์๋ก์ด ์ด๋ฒคํธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ ์ํจ๋ค.
๋ฆฌ๋ค์ด๋ ํธ์ ์์ฒญ ๋ฉ์๋๊ฐ GET์ผ๋ก ๋ณํ๊ณ ๋ฉ์์ง ๋ฐ๋๊ฐ ์ ๊ฑฐ๋ ์ ์์
ํญ์ ๋ฉ์๋๊ฐ ๋ณํ๊ณ ๋ณธ๋ฌธ์ด ์ ๊ฑฐ๋๋ ๊ฒ์ ์๋์ง๋ง ๋๋ถ๋ถ ํด๋น ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์๋ค.
๋ฆฌ๋ค์ด๋ ํธ์ ์์ฒญ ๋ฉ์๋์ ๋ฉ์์ง ๋ฐ๋ ์ ์ง
๋์ ์ฐจ์ด๋ฅผ ์ด์ ๊ณผ ๋์ผํ ํ๊ฒฝ์์ ์ ์ ๊ฐ ํน์ ์ด๋ฒคํธ ํ์ด์ง์ ๋ด์ฉ์ ์์ฑํด์ ํ์ธ๋ฒํผ์ ๋๋ ์ ๋๋ฅผ ์์๋ก ๋ค์ด๋ณด์.
// MARK: - 301
// ์ ์ ์์ฒญ - URL:/event
POST /event HTTP/1.1
name=hello&age=20
// ์๋ต
HTTP/1.1 301 Moved Permanently
Location: /new-event
// ์ ์ ์์ฒญ - URL:/event-new
GET /new-event HTTP/1.1
// MARK: - 308
// ์ ์ ์์ฒญ - URL:/event
POST /event HTTP/1.1
name=hello&age=20
// ์๋ต
HTTP/1.1 301 Moved Permanently
Location: /new-event
// ์ ์ ์์ฒญ - URL:/event-new
POST /event HTTP/1.1
name=hello&age=20
301์ ๊ฒฝ์ฐ ๋ฆฌ๋ค์ด๋ ํธ ์ดํ ์์ฒญํ๋ ๋ฉ์๋๊ฐ GET์ผ๋ก ๋ฐ๋๊ณ ๋ฉ์์ง๊ฐ ์ฌ๋ผ์ง๊ฒ์ ํ์ธ ํ ์ ์๋ค. ์ฆ ์๋กญ๊ฒ ๋ฆฌ๋ค์ด๋ ์ ๋ ํ์ด์ง์์ ๋ค์ ์ด๋ฒคํธ๋ฅผ ์ํ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๊ณ POST๋ฅผ ์์ฒญํด์ผ ํ๋ค.
๋ฆฌ๋ค์ด๋ ํธ์ ๋ฉ์๋๊ฐ GET์ผ๋ก ๋ณํ๊ณ ๋ณธ๋ฌธ์ด ์ ๊ฑฐ๋ ์ ์์ (301๊ณผ ์ ์ฌ)
๋ฆฌ๋ค์ด๋ ํธ์ ์์ฒญ ๋ฉ์๋์ ๋ณธ๋ฌธ ์ ์ง
๋ฆฌ๋ค์ด๋ ํธ์ ์์ฒญ ๋ฉ์๋๊ฐ GET์ผ๋ก ๋ณ๊ฒฝ
307, 303์ 301๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์คํ์์ผ๋ก๋ ์์ฒญ ๋ฉ์๋๊ฐ ๋ณ๊ฒฝ๋์ง๋ ์์ง๋ง ๊ฑฐ์ ๋ชจ๋ ์น ๋ธ๋ผ์ฐ์ ๋ค์ด GET์ผ๋ก ๋ฐ๊พธ๊ณ ์๋ค. ์ด๋ฌํ ๋ชจํธํจ์ ์์ ๊ธฐ ์ํด์ ์คํ์ ํ์คํ ์์ฒญ ๋ฉ์๋๊ฐ ๋ฐ๋๋ 303, ๊ทธ๋ ์ง ์์ 307์ด ๋ฑ์ฅํ๋ค.
๊ทธ๋ผ ์๊ตฌ์ ์ธ ๋ฆฌ๋ค์ด๋ ์ ๊ณผ ์ผ์์ ์ธ ๋ฆฌ๋ค์ด๋ ์ ์ ์ฐจ์ด์ ์ด ๋ฌด์์ผ๊น?
๋ฆฌ์์ค์ URI๊ฐ ์๊ตฌ์ ์ผ๋ก ๋ณ๊ฒฝ๋๋์ง ์ผ์์ ์ผ๋ก ๋ณ๊ฒฝ๋๋์ง๋ฅผ ๊ธฐ์ค์ผ๋ก ๋๋ ์ง๋ค. ํ์ง๋ง ํด๋ผ์ด์ธํธ ์ ์ฅ์์ ๋ณ๊ฒฝ๋ URI๋ก ๋ฆฌ๋ค์ด๋ ์ ๋๋ ๊ฒฐ๊ณผ๋ ๊ฐ์๋ฐ ์ ํํ๊ฒ ์ด๋ค ์ฐจ์ด๊ฐ ์๋๊ฑธ๊น?
๊ฒฐ๋ก ์ ์ผ๋ก ๋งํ๋ฉด ์ด ๋์ ๊ตฌ๋ณํ๋ ์ฃผ์ฒด๋ ์ฌ๋์ด ์๋๋ผ ๊ฒ์์์ง์ด๊ณ ํด๋ผ์ด์ธํธ์์๋ ์ฐจ์ด๋ฅผ ์ ์ ์๋ค.
์๊ตฌ ๋ค์ด๋ ์ ์ธ ๊ฒฝ์ฐ์๋ ๊ฒ์์์ง์์๋ URL์ด ๋ณ๊ฒฝ๋๊ณ ์ผ์์ ์ธ ๋ค์ด๋ ์ ์์๋ ๊ฒ์์์ง์์๋ URL์ด ๋ณ๊ฒฝ๋์ง ์๋๋ค.
๐ 301 vs 302 ์ํ ์ฝ๋ ์ฐจ์ด์ (SEO)
์์ ๋ธ๋ก๊ทธ์์๋ ์บ์ฑ์ ๋ํ ์ฐจ์ด๋ ์๋๋ฐ ํด๋น ๋ถ๋ถ์ ์บ์ฑ์ ๋ํด ์ข ๋ ๊ณต๋ถํ๊ณ ๋ค์ ์ ๋ฆฌ
์น ๋ธ๋ผ์ฐ์ ์์ ์๋ก๊ณ ์นจ์ ํ๋ฉด ๋ง์ง๋ง ์์ฒญ์ ๋ค์ ๋ณด๋ด๊ฒ ๋๋๋ฐ POST ๋ฉ์๋๋ก ์์์ ์ฃผ๋ฌธํ๊ณ ์๋ก๊ณ ์นจ ํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น? โ ์ค๋ณต ์ฃผ๋ฌธ์ด ๋ ์ ์๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์ 302(303)๋ฅผ ์ด์ฉํด์ ์ฃผ๋ฌธ ๊ฒฐ๊ณผ ํ๋ฉด์ GET ๋ฉ์๋๋ก ๋ฆฌ๋ค์ด๋ ํธ ์์ผ์ค๋ค. ์ดํ ์๋ก๊ณ ์นจํด๋ ๊ฒฐ๊ณผํ๋ฉด์ GET์ผ๋ก ๋ค์ ์กฐํํ๊ธฐ ๋๋ฌธ์ ์ค๋ณต ์ฃผ๋ฌธ์ ๋ฐฉ์งํ ์ ์๋ค.
ํด๋ผ์ด์ธํธ ์์ฒญ์ด ์๋ชป๋์ด์ ์๋ฒ๊ฐ ์์ฒญ์ ์ํํ ์ ์๋ ์ํ
ํด๋ผ์ด์ธํธ ์ค๋ฅ์ ์๋ฒ์ค๋ฅ๋ฅผ ๊ตฌ๋ถํ๋ ๊ธฐ์ค์ ๋์ผํ ๋ฐ์ดํฐ๋ก ์ฌ์๋ ์ผ์ด๋ฌ์ ๋ ํด๋ผ์ด์ธํธ ์ค๋ฅ๋ ์ด๋ฏธ ์๋ชป๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ณ์ํด์ ์คํจํ๊ณ ์๋ฒ ์ค๋ฅ๋ ์๋ฒ์ ์ํ๊ฐ ์์ ํ(?)๋๋ฉด ๋ณต๊ตฌ๋ ์ ์๋ค๋ ์ฐจ์ด๊ฐ ์๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ์๋ชป๋ ์์ฒญ์ ํด์ ์๋ฒ๊ฐ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์์
ํด๋ผ์ด์ธํธ๊ฐ ํด๋น ๋ฆฌ์์ค์ ๋ํ ์ธ์ฆ์ด ํ์ํจ
์ค๋ฅ ๋ฉ์์ง๊ฐ Unauthorized๊ฐ(์ธ๊ฐ)๋ก ๋์ด์์ง๋ง ์ธ์ฆ์ ๋ํ ์ค๋ฅ๋ฅผ ๋ํ๋
์๋ฒ๊ฐ ์์ฒญ์ ์ดํดํ์ง๋ง ์น์ธ์ ๊ฑฐ๋ถํจ โ ์ ๊ทผ ๊ถํ์ด ๋ถ์ถฉ๋ถํ ๊ฒฝ์ฐ
์์ฒญ ๋ฆฌ์์ค๊ฐ ์๋ฒ์ ์์
์๋ฒ ๋ด๋ถ ๋ฌธ์ ๋ก ์ค๋ฅ ๋ฐ์
์ ๋งคํ๋ฉด 500๋ฒ ์๋ฌ๋ฅผ ์ฃผ๋๋ฐ ์ด๋ ์์ํ ์๋ฒ์ค๋ฅ์ธ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ๋๋ก ์งํฅํด์ผ ํ๋ค.
์๋ฅผ ๋ค์ด์ 19์ธ ๋ฏธ๋ง์ ์ฌ์ฉ์๊ฐ ์ฃผ๋ฅ๋ฅผ ์ฃผ๋ฌธํ์ ๋ ์๋ฒ์์ ์ ์ ์ ๋์ด ๋๋ฌธ์ ์ ์์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ์๋ 2xx, 4xx๋ฅผ ์ฌ์ฉํด์ ํํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ธ๋ค.
์๋น์ค ์ด์ฉ ๋ถ๊ฐ
์ผ์์ ์ธ ๊ณผ๋ถํ ํน์ ์์ ๋ ์์ ์ผ๋ก ์๋ฒ๋ฅผ ๋ค์ด ์์ผ๋์ ๊ฒฝ์ฐ ์ฌ์ฉํ๊ณ Retry-After ํค๋๋ก ๋ณต๊ตฌ ์๊ฐ์ ๋ณด๋ผ ์๋ ์๋ค.
ํด๋ผ์ด์ธํธ ๊ฐ๋ฐ์๋ก ๋ฐฑ์๋ ๊ฐ๋ฐ์๊ฐ ๋ง๋ค์ด์ค API๋ฅผ ์ฐ๋ฉด์ ๋์ถฉ ์๊ณ ์๋ HTTP Method๋ฅผ ์ ๋๋ก ์ ๋ฆฌํด๋ณด์
์์ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ฐ์ง API URL ๋ง๋ค์ด๋ฌ๋ผ๋ ์์ฒญ์ ๋ฐ์์ ๋ URL์ ์ด๋ป๊ฒ ๋ง๋ค์ด์ผ ํ ๊น?
/read-member-list
/read-member-by-id
/create-member
/update-member
/delete-member
์์ ๊ฐ์ด ๋ค์ด๋ฐ์ ํตํด์ URL์ ์ค๊ณํ๋ฉด ์ด๋จ๊น? URL ํํ๋ง์ผ๋ก ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋์ ์์ฒด๋ฅผ ์ ์ ์์ผ๋๊น ๊ด์ฐฎ์ง ์์๊น?
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด NO์ด๋ค. URI๋ฅผ ์ค๊ณํ ๋ ๊ฐ์ฅ ์ค์ํ ํฌ์ธํธ๋ ๋ฆฌ์์ค ์๋ณ
์ด๋ค.
์์ ๋ชจ๋ ๊ธฐ๋ฅ์์ ํ์
์ ๋ํ ์์
์ ํ๊ณ ์์์ ์ ์ ์๊ณ ํ์ = ๋ฆฌ์์ค
์์ ์ ์ ์๋ค. ๋ฐ๋ผ์ ํ์์ด๋ผ๋ ๋ฆฌ์์ค๋ฅผ URI์ ๋งคํ ์์ผ์ ์๋ณํ ์ ์๋ ํํ๋ก ๊ตฌ์ฑ๋์ด์ผ ํ๋ค.
/members
/members/{id}
/members/{id}
/members/{id}
/members/{id}
๊ทธ๋ผ ํ์ ์กฐํ, ๋ฑ๋ก, ์์ , ์ญ์ ์ ๊ฐ์ ํ์๋ ์ด๋ป๊ฒ ๊ตฌ๋ถํด์ผ ํ๋ ๊ฑธ๊น?
โ ์ด๋ ์ฌ์ฉ๋๋ ๊ฒ์ด HTTP Method์ด๋ค.
๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ์๋ฉด
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS
CONNECT
TRACE
๋ฉ์๋์ ์ข ๋ฅ๋ ์์ ๊ฐ๊ณ ์๋ 4๊ฐ๋ ํ๋ฒ๋ ๋ณธ์ ์ด ์์๋๋ฐ ์กด์ฌ ์ ๋๋ง ์๊ณ ๋๊ธฐ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
POST๋ ์์ฒญ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฟ๋ง ์๋๋ผ ์๋ก์ด ๋ฆฌ์์ค๋ฅผ ๋ฑ๋กํ๋ ๋ฑ ๊ฑฐ์ ๋ชจ๋ ์ญํ ์ ์ํํ ์ ์๋ค.
์ด์ ๋ฉด์ ์์ PUT, POST์ ์ฐจ์ด์ ์ ๋ํด์ ์ง๋ฌธ ๋ฐ์ ์ ์ด ์์๋๋ฐ ์ด์ ์ด๋ ๊ฒ ๋ต๋ณํ ์ ์์ ๊ฒ ๊ฐ๋ค.
๋ ๋ค ์๋ฒ์ ์ด๋ค ๋ฆฌ์์ค๋ฅผ (๋์ฒด, ์์ฑ) ํ ๋ ์ฌ์ฉํ ์ ์๋ค๋ ๊ณตํต์ ์ด ์์ง๋ง POST์ ๊ฒฝ์ฐ URI์ ํด๋น ๋ฆฌ์์ค์ ์์น๋ฅผ ๋ชจ๋ฅด๊ณ PUT์ URI์ ํด๋น ๋ฆฌ์์ค ์์น๋ฅผ ๋ช ์ํด์ผ ํ๋ค๋ ์ฐจ์ด๊ฐ ์๋ค.
POST/members
Content-Type: json
{
"username": "young",
"age": 20
}
// members/100์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ด์ฉํด์ ์์ฑ
PUT/members/100
Content-Type: json
{
"username": "young",
"age": 20
}
// members/100๋ฅผ ํด๋น ๋ฐ์ดํฐ๋ก ๋์ฒด
๊ทธ๋ฆฌ๊ณ ์์ ํ ๋์ฒด ํ๋ค๋ ์๋ฏธ๋ ํด๋น ์์น์ ๋ฆฌ์์ค๋ฅผ ์ผ๋ถ๋ถ๋ง ๋ณ๊ฒฝํ ์ ์์์ ์๋ฏธํ๋ค.
// members/100(DB)
{
"username": "young",
"age": 20
}
PUT/members/100
Content-Type: json
{
"age": 30
}
// members/100๋ฅผ ํด๋น ๋ฐ์ดํฐ๋ก ๋์ฒด
// members/100(DB)
{
"age": 30
}
์์ ๊ฐ์ด age์ ๊ฐ๋ง ๋ถ๋ถ ์์ ํ ์๋ ์๊ณ ์์ ํ ๋์ฒดํด์ username์ ๊ฐ์ด ์ฌ๋ผ์ง๋ค.
์์ PUT์์ ๋ถ๋ถ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํ๋ ๋ถ๋ถ์ PATCH๋ฅผ ํตํด์ ํด๊ฒฐํ ์ ์๋ค.
ํธ์ถํด๋ ๋ฆฌ์์ค๊ฐ ๋ณ๊ฒฝ๋์ง ์๋์ง?
1๋ฒ ํธ์ถ๊ณผ n๋ฒ ํธ์ถ์ ๊ฒฐ๊ณผ๊ฐ ๊ฐ์์ง?
์๋ต ๊ฒฐ๊ณผ ๋ฆฌ์์ค๋ฅผ ์บ์ํด์ ์ฌ์ฉํด๋ ๋๋์ง?
GET, POST, PUT, PATCH, DELETE 5๊ฐ์ ๋ฉ์๋๋ฅผ ๊ธฐ์ค์ผ๋ก 3๊ฐ์ ์์ฑ๊ฐ์ด ์ด๋ป๊ฒ ๋ ๊น?
POST, PUT, PATCH, DELETE ๋ฉ์๋๋ ํธ์ถํ ๋ ๋ง๋ค ๋ฆฌ์์ค๊ฐ ๋ณ๊ฒฝ๋ ์ ์๋ ๊ฐ๋ฅ์ฑ์ด ์์ง๋ง GET์ ๋ฆฌ์์ค๋ฅผ ์กฐํ๋ง ํ๊ธฐ ๋๋ฌธ์ ์์ ํ๋ค
๋ฉฑ๋ฑ์ฑ์ด ์๋ค๋ ๊ฒ์ ํด๋ผ์ด์ธํธ์์ ์๋ฒ๊ฐ ์ ์ ์๋ต์ ์ฃผ์ง ๋ชปํ์๋, ํด๋ผ์ด์ธํธ๊ฐ ๋์ผํ ์์ฒญ์ ์๋์ผ๋ก ํด๋ ๋๋์ง (n๋ฒ)์ ๋ํ ๊ธฐ์ค์ด ๋๋ค.
GET, HEAD, POST, PATCH 4๊ฐ์ง๊ฐ ์ด๋ก ์ ์บ์ฑ ๊ฐ๋ฅํ๋ค ํ์ง๋ง POST, PATCH๋ ๋ฉ์์ง ๋ฐ๋ ๊น์ง ์บ์ ํค๋ก ๊ณ ๋ คํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ์ ์ผ๋ก GET, HEAD 2๊ฐ๋ง ์บ์ฑ์ ์ด์ฉํ๊ณ ์๋ค.
ํ์ฌ์์ ์๋ฒ ๊ฐ๋ฐ์๋ค์ด ์ฌ์ฉํ๋ URI์ URL์ ๋ํ ์ฐจ์ด์ ์ ํ์ธํด๋ณด์
์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ๋ค
ex) http, https, ftp
http = 80, https = 443๋ฅผ ์ฌ์ฉ (์ผ๋ฐ์ ์ผ๋ก ์๋ต)
GET
์์ ์์ฃผ ์ฌ์ฉํ์์์ผ๋ฐ์ ์ผ๋ก ๋น๋์นญํค ์๊ณ ๋ฆฌ์ฆ์์๋ ๊ณต๊ฐํค๋ฅผ ํตํด์ ์ํธํ๋ฅผ ์งํํ๊ณ ๋น๋ฐํค๋ฅผ ํตํด์ ๋ณตํธํ๋ฅผ ์งํํ๋ค.
(์๋ช ๊ณผ ๊ฒ์ฆ๊ณผ์ ์์๋ ๋น๋ฐํค๋ฅผ ํตํด์ ์๋ช ๊ณผ์ ์ ์ํธํ ์์ผ์ ๋ณด๋ด๊ณ ๊ณต๊ฐํค๋ฅผ ํตํด์ ๋ณตํธํ๋ฅผ ์งํํ๋ค)
โ ์ด์ ๋ํ ์์ธํ ๋ด์ฉ์ ๋์นญํค์ ๋น๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ ๋ํ ์ ๋ฆฌํ ๋ด์ฉ์ ํ์ธํ๋ฉด ๋๋ค.
/// RSA - Encrypt plain string to rsa base64 String
func encryptByRSA(
plainString: String
) -> String? {
var error: Unmanaged<CFError>?
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256
guard
let publicKey = self.publicKey,
SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm),
let plainData = plainString.data(using: .utf8),
let ciperData = SecKeyCreateEncryptedData(
publicKey,
algorithm,
plainData as CFData,
&error
)
else { return nil }
return cfDataTobase64String(ciperData)
}
Public Key๋ฅผ ํตํด์ ํ๋ฌธ์ ์ํธํ ํ๋ ๊ตฌํ ์์ฒด๋ ์ฝ๋๋ฅผ ํตํด์๋ ์ฝ๊ฒ ํ์ธํ ์ ์๊ณ Security์์ ์ ๊ณตํด์ฃผ๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋จํ๋ค. ํ์ง๋ง ECDSA์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํ ์์ฒด๋ณด๋ค๋ ์๋ฒ ํน์ ์ด๋ฏธ ๊ตฌํ๋ ๊ณณ๊ณผ ์คํ์ ๋ง์ถ๋ ๊ณผ์ ์ด ์ค์ํ๋ค.
RSA ์๊ณ ๋ฆฌ์ฆ์ ํตํ ์ํธํ ๋ณตํธํ ๊ณผ์ ์์๋ ํจ๋ฉ์ต์ ์ ์ค์ ํด ์ค ์ ์๋๋ฐ ์ด๋ฅผ ๋ค๋ฅธ ๊ณณ๊ณผ ๋์ผํ๊ฒ ์ค์ ํด์ผ ์๋ก ์ ์์ ์ธ encrypt, decrypt๋ฅผ ์ํํ ์ ์๋ค.
์ฝ๋์์๋ ํ์ธํ ์ ์๋ฏ์ด SecKeyAlgorithm ์ค์ ์ ํตํด์ ๋ค์ํ ํจ๋ฉ, ํด์ํจ์ ์ต์ ๋ค์ด ์๋๋ฐ ์ด ์ค์์ ๋ค๋ฅธ ๊ณณ๊ณผ ๋์ผํ ์ต์ ์ ์ฐพ์์ ์ฌ์ฉํด์ผ ํ๋ค.
(GPT ์ฝ๋๋ฅผ ์ฐธ๊ณ ํ์ ๋ SecPadding
์ ํตํด์ ํจ๋ฉ์ต์
์ ์ค์ ํ๋ ์ฝ๋๋ค์ด ๋ง์๋๋ฐ ์์ฝ๊ฒ๋ iOS15 ์ดํ์ deprecated ๋์๋ค)
์ฝ๋๋ฅผ ๋ณด๋ฉด encrypt ํ๋ ค๋ ํ๋ฌธ์ ๋ฐ์ดํฐ๋ก ๋ง๋ค๊ณ public key๋ฅผ ์ด์ฉํด์ ์ฐ๋ฆฌ๊ฐ ์ค์ ํ ์ต์ ์ ํตํด์ encrypt๋ฅผ ์ํํ ๊ฒฐ๊ณผ๊ฐ CFData type์ผ๋ก ciperData์ ๋ด๊ธฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ผ๋ฐ์ ์ผ๋ก ์๋ฒ์ ํต์ ํ ๋ Data Type๋ณด๋ค๋ Hex String, Base64String ํ์ ์ผ๋ก ๋ณด๋ด์ค๋ค.
private func cfDataTobase64String(
_ cfData: CFData
) -> String? {
let data = cfData as Data
let base64String = data.base64EncodedString()
return base64String
}
/// RSA - Decrypt rsa base64 String to plain String
func decryptByRSA(
encodedString: String
) -> String? {
var error: Unmanaged<CFError>?
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256
guard
let privateKey = self.privateKey,
SecKeyIsAlgorithmSupported(privateKey, .decrypt, algorithm),
let encodedData = base64StringToCFData(encodedString),
let ciperData = SecKeyCreateDecryptedData(
privateKey,
algorithm,
encodedData,
&error
)
else { return nil }
return cfDataToString(ciperData)
}
๋ณตํธํ ํ๋ ์ฝ๋๋ ์ํธํ๋ String์ CFData๋ก ๋ณ๊ฒฝํ๊ณ private key๋ฅผ ์ฌ์ฉํ๋ค๋ ์ ์ด์ธ์๋ encrypt๊ณผ ๋ณ๋ฐ ๋ค๋ฅด์ง ์๋ค.
private func cfDataToString(
_ cfData: CFData
) -> String? {
let length = CFDataGetLength(cfData)
if let bytes = CFDataGetBytePtr(cfData) {
let data = Data(bytes: bytes, count: length)
return String(data: data, encoding: .utf8)
} else {
return nil
}
}
decrypt ๊ฒฐ๊ณผ๋ก๋ base64 Stringํ์ ์ด ์๋ String ํ๋ฌธ์ด ๋์ค๋๋ก ์ค์ ํด์ผ ํ๋ค.
RSA๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
ํฌ๊ฒ ๋ ๊ฐ์ง ํํ๊ฐ ์กด์ฌํ๊ฒ ๋๋๋ฐ ์ ์์ ๊ฒฝ์ฐ์๋ ์๋ฒ๋ก ๋ณด๋ด๋ request body๋ฅผ ์ํธํ ์ํค๋ ๋ก์ง, ํ์๋ ์๋ฒ๋ก ๋ถํฐ ์ํธํ ๋ response body๋ฅผ ๋ณตํธํ ์ํค๋ ๋ก์ง์ด ํ์ํ๋ค.
์ ์์ ๊ฒฝ์ฐ์๋ ์๋ฒ๋ก ๋ถํฐ public key๋ฅผ SecKey ํ์ ์ผ๋ก ๋ณ๊ฒฝํ๋ ๋ก์ง์ด ์ถ๊ฐ์ ์ผ๋ก ๊ตฌํ๋์ด์ผ ํ๋ค. ์๋ฒ์์ ์ด๋ค ํ์์ผ๋ก public key๊ฐ ์ฌ์ง๋ ์ํฉ๋ง๋ค ๋ค๋ฅด๊ฒ ์ง๋ง ์ด ๊ณผ์ ์์ representation์ ์ค์ ํ ์ ์๋ ๋ฌธ์ ๋๋ฌธ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ์ ๊ฒฐ์ ํ๋ค.
private func stringToSecKey(
_ privateKey: String
) -> SecKey? {
let keyAttributes: [CFString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: 2048
]
var error: Unmanaged<CFError>?
if let privateKeyData = Data(base64Encoded: privateKey),
let secKey = SecKeyCreateWithData(
privateKeyData as CFData,
keyAttributes as CFDictionary,
&error
) {
return secKey
} else {
return nil
}
}
๋ง์ฝ raw Representation์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด ์์ ์ฝ๋๋ฅผ ํตํด์ SecKey๋ฅผ ์์ฑํ ์ ์๋ค.
ํ๋ก์ธ์ค๋ ์ด์์ฒด์ ๋ก ๋ถํฐ ์์์ ํ ๋น๋ฐ๋ ์์
์ ๋จ์
์ด๊ณ
์ด์์ฒด์ ๋ก ๋ถํฐ ํ ๋น๋ฐ์ ์์์ ๋
๋ฆฝ๋ Heap, Data, Stack, Code
๋ก ๊ตฌ์ฑ๋์ด ์๋ค๊ณ ํ์ตํ์ต๋๋ค.
Read-Only
์ ์ญ๋ณ์์ ์ ์ ๋ณ์
๊ฐ ์ ์ฅ๋๋ ์์ญ -> ํ๋ก๊ทธ๋จ์ด ์คํ๋๋ ๋์ ํญ์ ์ ๊ทผ ๊ฐ๋ฅํ ๋ณ์๊ฐ ์ ์ฅRead-Write
Stack Overflow
๋ฐ๋ผ์ ์ด๋ ํ์ชฝ์ ์์ญ์ด ์๋๊ณต๊ฐ์ ์นจ๋ฒํ๋ ์ผ์ด ์๊ธธ ์ ์๊ณ ์ด๋ฅผ Heap Overflow, Stack Overflow
๋ผ๊ณ ํ๋ค.
๋ณดํต ๋ฐฑ์ค์์ ์๊ณ ๋ฆฌ์ฆ ๋ฌธ์ ๋ฅผ ํ ๋ค ๋ณด๋ฉด ์ฌ์ฌ์ฐฎ๊ฒ ๋ฉ๋ชจ๋ฆฌ ์ด๊ณผ๋ฅผ ๊ฒฝํํ ์ ์์ต๋๋ค. ๋ฌผ๋ก ์ค์ ๋ก ๋ฌธ์ ์์ ์๊ตฌํ๋ ๋ฉ๋ชจ๋ฆฌ ๋ณด๋ค ๋ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ผ ์๋ ์์ง๋ง ๋๋ถ๋ถ ์ง์ญ๋ณ์๋ก ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ์ญ๋ณ์๋ก ์์ ํ๋ฉด ์์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํผํ ์ ์์์ต๋๋ค.
์ด๋ ๋ณดํต OS์์ ์ฑ๋ฅ์์ ์ด์ ๋ก Stack ์์ญ์ ๋ฉ๋ชจ๋ฆฌ ์ ํ์ ๊ฑธ์ด๋๊ธฐ ๋๋ฌธ์
๋๋ค. (์๋์ฐ: 1MB, ๋ฆฌ๋
์ค: 8MB)
๋ฐ๋ผ์ ์ ์ญ๋ณ์๋ก ์ ์ธํ ๋ฐ์ดํฐ๋ค์ Data ์์ญ์ ์ ์ฅ๋๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ ๋ฌธ์ ๋ฅผ ํผํ ์ ์์์ต๋๋ค.
Swift์์๋ ํด๋์ค๋ฅผ ํตํด์ ์ธ์คํด์ค๋ฅผ ์์ฑํ ๊ฒฝ์ฐ
Heap ์์ญ
Stack ์์ญ
์ ์ ์ฅ๋๊ณ RC๊ฐ 0์ด๋๋ ์๊ฐ์ ์๋์ผ๋ก Heap ์์ญ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๋ ARC ๋ผ๋ ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
C์ธ์ด์ ๊ฒฝ์ฐ, Heap ์์ญ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ Calloc, Malloc
๊ณผ ๊ฐ์ ํจ์๋ฅผ ํตํด์ ์ฌ์ฉ์๊ฐ ์ง์ ํ ํฌ๊ธฐ ๋งํผ์ Heap ์์ญ
์ ํ ๋นํ๊ณ Stack ์์ญ์๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฃผ์๊ฐ๋ง์ ๊ฐ์ง๊ณ ์๊ฒ ๋ฉ๋๋ค.
ํ์ง๋ง C์์๋ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ํผํ๊ธฐ ์ํด์๋ Swift์ ๋ฌ๋ฆฌ Heap์์ญ
๋ฉ๋ชจ๋ฆฌ ํด์ ๋ฅผ ์ํด์ free ํด์ฃผ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค.
ํ๋ก์ ํธ ์งํ ์ค FCM์ ์ด์ฉํด ๋ ๋ผ์จ ํธ์๋ฅผ ํด๋ฆญ ํ์ ๋ ๊ด๋ จ ๋ด์ฉ์ ์ฑ ๋ด์์ ํ๋ฉด์ ํ ํด์ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ด ํ์ํ๋ค. (ex ์นด์นด์คํก ํธ์๋ฅผ ๋๋ ์ ๋ ํด๋น ๋ํ๋ฐฉ์ผ๋ก ์ด๋)
์ด๋ฐ ๊ธฐ๋ฅ๋ฑ์ ์ฒ๋ฆฌํ ๋ ๋ฅ๋งํฌ, ์ฑ๋งํฌ, ์ ๋๋ฒ์
๋งํฌ
๋ฑ๋ฑ์ ๋ค์ด๋ณด๊ธด ํ์ง๋ง ํ๋ฒ๋ ๊ตฌํํด๋ณธ์ ์ ์์ด์ ์ด๋ฒ๊ธฐํ์ ํ๋ฒ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค. SwiftUI๋ฅผ ์ด์ฉํด์ ๊ตฌํํด์ผ ํ๋ค๋ ์ ์ฝ์ฌํญ์ด ์์ด ์์ฝ๊ธด ํ์ง๋ง ๊ธฐํ๊ฐ ๋๋ค๋ฉด UIKit์ ์ด์ฉํ ํ๋ก์ ํธ์๋ ์ ๋ฆฌํ๋ ์๊ฐ์ ๊ฐ์ ธ๋ณผ ์์ ์ด๋ค.
์ฉ์ด์ ๋ฆฌ ๋ถํฐ ํ๊ณ ๋์ด๊ฐ๋ณด์
ํน์ ์ฃผ์ ํน์ ๊ฐ์ ์ ๋ ฅํ๋ฉด ์ฑ์ด ์คํ๋๊ฑฐ๋ ์ฑ ๋ด ํน์ ํ๋ฉด์ผ๋ก ์ด๋์ํค๋ ๊ธฐ๋ฅ์ ์ํ
์ฐ์ ๋ฅ๋งํฌ, ์ฑ๋งํฌ, ์ ๋๋ฒ์ ๋งํฌ๋ฅผ ๊ฐ๊ฐ ๊ฐ๋ณ์ ์ผ๋ก ์๊ฐํ๊ณ ์์๋๋ฐ ๋ฅ๋งํฌ๋ผ๋ ํฐ ๋ฒ์ฃผ์์์ ์ฑ๋งํฌ, ์ ๋๋ฒ์ ๋งํฌ๋ก ๋๋์ด์ง๋ค. (๋ค์ด๋๋ฏน ๋งํฌ, deferred ๋ฅ๋งํฌ๋ ์ถํ ์ ๋ฆฌ)
๊ทธ๋ฆผ์ผ๋ก ๋ํ๋ด๋ฉด ์์ ๊ฐ์ ํํ์ด๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ํน์ ๋งํฌ๋ฅผ ์ ๋ ฅ(ํด๋ฆญ) ํ์ ๋ ๊ฐ๋ฐ์๊ฐ ์ํ๋ ํ์ด์ง๋ก ์ฌ์ฉ์๋ค์ ์๋ดํ๋ ๊ธฐ์ ์ ๋ฅ๋งํฌ๋ผ๊ณ ํ๊ณ , ๋ฅ๋งํฌ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ด ์์ ๊ฐ์ด ์กด์ฌํ๋ค.
(์ฑ๋งํฌ๋ AOS์์ ์ฌ์ฉํ๋ ์ฉ์ด)
์ผ๋ฐ์ ์ด๊ณ ์ด๊ธฐ์ ์ฌ์ฉ๋ ๋ฅ๋งํฌ ๊ตฌํ ๋ฐฉ์์ผ๋ก ์ฑ์ ์คํด๊ฐ์ ๋ฑ๋กํด์ ์ฌ์ฉํ๋ค.
{ Scheme : ์ฑ์ ๊ตฌ๋ถ }:// { Path : ์ฑ๋ด ํ์ด์ง ๊ตฌ๋ถ }
kakaotalk://me
sms://
์์ ๋งํฌ๋ฅผ ์ฌํ๋ฆฌ์ ์ ๋ ฅํด๋ณด์. ์นด์นด์คํก์ด ์ค์น๋์ด ์๋ ํด๋ํฐ์ผ ๊ฒฝ์ฐ ์นด์นด์คํก์ด ์ด๋ฆฌ๋ ๊ฒ์ ํ์ธํ ์ ์์๊ฒ์ด๋ค.
๊ฐ๋จํ๊ฒ ์์ ๋ด์ฉ์ ํ๋ฒ ๊ตฌํํด๋ณด์(์ ๋๋ฆฌ๋์ ๋ฅ๋งํฌ ๊ฐ์๋ฅผ ์ ๋ฆฌํ ๋ด์ฉ์ ๋๋ค.)
์ ๋๋ฆฌ๋ ๊ฐ์ ๋งํฌ](https://www.youtube.com/watch?v=kjDl_15fOEQ)
์ฐ์ , ์์ ๊ฐ์ด ์คํด์ ๋ฑ๋กํด์ค๋ค.
enum TabIdentifier: Hashable {
case todo
case profile
}
enum PageIdentifier: Hashable {
case todoItem(id: UUID)
}
extension URL {
var isDeepLink: Bool {
return scheme == "deeplink-bran"
}
// ์ด๋ค ํญ์ ๋ณด์ฌ์ค ๊ฒ์ธ๊ฐ?
var tabIdentifier: TabIdentifier? {
guard isDeepLink else { return nil }
// deeplink-bran:// { host }
switch host {
case "todo":
return .todo
case "profile":
return .profile
default:
return nil
}
}
//deeplink-bran://todo/ED7E277E-1A5E-4197-AF6D-1D9ED1BFFF9F
var detailPage: PageIdentifier? {
guard
let tabIdentifier = tabIdentifier,
pathComponents.count > 1,
let uuid = UUID(uuidString: pathComponents[1])
else { return nil }
switch tabIdentifier {
case .todo:
return .todoItem(id: uuid)
case .profile:
return nil
}
}
}
URL์ด ๋ค์ด์จ ๊ฒฝ์ฐ { Scheme }:// { Path }
ํํ์ URL์ธ์ง ํ์ธํ๊ณ , Path๋ฅผ ํตํด์ ์ฌ์ฉ์์๊ฒ ์ ์ ํ ํ์ด์ง๋ก ๋ณด๋ด์ฃผ๋ ๊ณผ์ ์ด ํ์ํ๋ค.
์์ ์ฝ๋๋ Scheme:// {TabIdentifier} / {PageIdentifier?} ํํ๋ก ๋ค์ด์จ URL์์ ์ฌ์ฉ์๋ฅผ ์ ์ ํ ํ์ด์ง์ ๋ณด๋ด์ฃผ๊ธฐ ์ํด ํ์ํ ๋ฐ์ดํฐ๋ค์ ๋ฝ์ ๋ผ ์ ์๋๋ก extension์ผ๋ก ๊ตฌํํ ์ฝ๋์ด๋ค.
struct TodoTabView: View {
let todoModels: [TodoListItem] = [...]
@State
var selectedTab: TabIdentifier = .todo
var body: some View {
tabSection
.onOpenURL { url in
guard let tabId = url.tabIdentifier else { return }
selectedTab = tabId
}
}
}
extension TodoTabView {
@ViewBuilder
var tabSection: some View {
TabView(selection: $selectedTab) {
TodoView(todoListItems: todoModels)
.tabItem {
Image(systemName: "tray")
Text("TODO")
}
.tag(TabIdentifier.todo)
ProfileView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag(TabIdentifier.profile)
}
}
}
์ฐ๋ฆฌ๊ฐ ์ค์ ํ Scheme://Path ํํ์ URL์ด ๋ค์ด์์ ๊ฒฝ์ฐ, ์ด๋ป๊ฒ ํ๋ฉด ์ ํ๋ฅผ ์ฒ๋ฆฌํด์ค ์ ์์๊น?
์์ธํ๊ฒ ๋ณด์ง ์์์ง๋ง UIKit์์๋ SceneDelegate, AppDelegate์์ ์์ ๋ด์ฉ์ ์ฒ๋ฆฌํด์ฃผ๊ณ ์๋๊ฒ ๊ฐ๋ค. SwiftUI์์๋ onOpenURL์ด๋ผ๋ modifier๋ฅผ ํตํด์ ๊ฐํธํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
ํด๋น ํ๋ก์ ํธ๋ ์์ ๊นํ๋ธ ๋งํฌ์์ ์ ์ฒด๋ฅผ ํ์ธ ํ ์ ์๋ค. ์์ ํ๋ก์ ํธ์์ URL Scheme์ ํตํด ๊ฐ๋ฐ์๊ฐ ์ค์ ํ ํญ๋ฐ & ๋ค๋น๊ฒ์ด์ ํธ์ฌ๊ฐ ์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
ํ์ง๋ง ์ฒ์ ๋ฅ๋งํฌ ๊ทธ๋ฆผ์์ ๊ฐ์
์ด ๊ฑธ๋ฆฐ๋ค. URL Scheme์๋ ์ด๋ค ๋ฌธ์ ๊ฐ ์์ด์ ์ ๋๋ฒ์
๋งํฌ๋ผ๋ ๊ฐ๋
์ด ๋์ค๊ฒ ๋๊ฑธ๊น? ์ด๋ ์์ ํ๋ก์ ํธ๋ฅผ ์๋ฆฌ์กฐ๋ฆฌ ํ
์คํธ ํด๋ณด๋ค ๋ณด๋ฉด ์์ธ๋ก ์ฝ๊ฒ ๋ฐ๊ฒฌํ ์ ์๋ค.
์ฑ์ด ์ค์น๋์ด ์์ง ์๋ ๊ฒฝ์ฐ, ์ฃผ์๊ฐ ์ ํจํ์ง ์๊ธฐ ๋๋ฌธ์ Safari๊ฐ ํด๋น ํ์ด์ง๋ฅผ ์ด ์ ์๋ค๋ ์๋ฌ ๋ฉ์์ง๊ฐ ๋์ค๊ณ , ๋ฒ๋ค identifier๋ฅผ ๋ฐ๊ฟ์ ๋์ผํ Scheme์ด ๋์ผํ ์ฌ๋ฌ๊ฐ์ ์ฑ์ ๋ง๋ค๊ณ ํ ์คํธ ํ๋ฉด ์ด๋ค ์ฑ์ด ์ด๋ฆด ์ง ์ฐ๋ฆฌ๊ฐ ๋ณด์ฅํ ์ ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋๋ฐ ์ด Scheme์ ์ฌ์ฉ์๊ฐ ์ฌ์ฉํ๊ณ ์๋ ์ฑ์์ Identifiable๋ฅผ ๋ณด์ฅ ํ ์ ์๋ค.
๋ค์ ํ ์ด ํ๋ก์ ํธ์์๋ ์ ๋๋ฒ์ ๋งํฌ๋ฅผ ๊ฐ๋จํ๊ฒ ๊ตฌํํด๋ณด๊ณ ์ ๋ฆฌํด๋ณผ ์์ ์ด๋ค.
์ฝ๋๋ฅผ ๋ณด๋ค๋ณด๋ฉด ํจ์๊ณผ ๊ฐ์ด <>๋ก ๊ฐ์ธ์ง ํํ๋ฅผ ์ฝ๊ฒ ์ฐพ์๋ณผ ์ ์๋ค. ์ด๋ T๋ ํ์ ํ๋ผ๋ฏธํฐ๋ก ์ด๋ค ํ์ ์ โ์ด๋ฆ'์ ์๋ฏธํ๋ค.
func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
๋ง๊ทธ๋๋ก ์ด๋ค โํ์ โ ์ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ ์ปดํ์ผ ๊ณผ์ ์์ ํด๋น ํ์ ์ด ์ด๋ค ํ์ ์ธ์ง ๊ฒฐ์ ๋์ง ์๊ณ ๋ฐํ์ ๊ณผ์ ์์ ํ์ ์ ๊ฒฐ์ ํ๋ค
์๋ฅผ ๋ค์ด์, C++ STL์ ์๋ Stack์ ๊ตฌํํ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ ๋ค๋ฆญ ํ์ ์ ์ด์ฉํด์ ํ์ ์ ๋ฌด๊ดํ Stack์ ๋ง๋ค ์ ์๋ค.
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
์๋์ ๊ฐ์ ํํ๋
struct TextFieldCustomViewWrapper<KeyboardView: View>: UIViewRepresentable {
}
KeyboardView ๋ผ๋ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ๋๋ฐ KeyboardView๋ View ํ๋กํ ์ฝ์ ์ฑํํ๊ณ ์๋ ์ด๋ค ํ์
์ ์๋ฏธํ๋ค. ์ด๋ ๊ฒ ํน์ ํ๋กํ ์ฝ์ ์ค์ํด์ผ๋ง ์ ๋ค๋ฆญ์ ์ฌ์ฉํ ์ ์๋๋ก ํ์ ์ ์ฝ์ ์ค ์ ์๋ค.
Swift ์ Dictionary ๊ตฌ์กฐ์ฒด๋ฅผ ๋ค์ฌ๋ค ๋ณด๋ฉด ์๋์ ๊ฐ์ด ํ์ ์ ์ฝ์ด ๊ฑธ๋ ค์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
@frozen struct Dictionary<Key, Value> where Key : Hashable
์ฆ Key๊ฐ Hashable ํ๋กํ ์ฝ์ ์ฑํํ๋ ํ์ ์ธ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ ์ ์๋๋ก ์ ์ฝ์ด ๊ฑธ๋ ค์๋ค.
protocol Publisher {
associatedtype Output
associatedtype Failure : Error
func receive<S>(subscriber: S)
where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
ํ๋กํ ์ฝ์ ๋ณด๋ค๋ณด๋ฉด associatedType์ด๋ผ๋ ํค์๋๋ฅผ ๋ณผ ์ ์๋ค. associated Type
์ด๋๊น ์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์๊ฐํ๋ Type์์ ์ ์ถํ ์ ์๋ค.
์์์ ํจ๊ป ๋ณด๋ฉด ์ด๋ค ์๋ฏธ๋ฅผ ๊ฐ์ง๋์ง ์ ์ ์๋ค.
protocol BranProtocol {
associatedtype MyType
var name: MyType { get }
}
struct Bran: BranProtocol {
var name: String {
return "Sangwon"
}
}
BranProtocol์ ์ฑํํ๋ฉด MyType์ name์ ์ ์ํด์ผ ํ๋๋ฐ String ํ์ ์ด์ธ์๋ ๋ค๋ฅธ ๋ค์ํ ํ์ ์ name์ด ๋ ์ ์๋ ๊ฐ๋ฅ์ฑ์ด ์์ ๋ ์์ ๊ฐ์ด associatedtype์ ์ฌ์ฉํ ์ ์๋ค.
(ํ๋กํ ์ฝ์์ ์ฌ์ฉํ๋ ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ์๊ฐ!)
์์ ์ ๋ค๋ฆญ์์ ๋ดค๋ ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฐ๊ดํ์
์๋ ํ๋กํ ์ฝ์ ํตํด์ ์ ์ฝ์ ์ค ์ ์๋ค.
์ฐ์ ํค์๋ ์ค์ฌ์ผ๋ก ๋จผ์ ์ ๋ฆฌํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
ํ๋ก๊ทธ๋จ(Program)
: ์ด๋ค ์์
์ ์ํด ์คํํ ์ ์๋ ํ์ผํ๋ก์ธ์ค(Process)
: ์ด์์ฒด์ ๋ก๋ถํฐ ์์์ ํ ๋น๋ฐ๋ ์์
์ ๋จ์์ค๋ ๋(Thread)
: ํ๋ก์ธ์ค ๋ด์์ ์คํ๋๋ ์ฌ๋ฌ ํ๋ฆ์ ๋จ์
ํ๋ก์ธ์ค
๋ ๊ฐ๊ฐ์ ๋ ๋ฆฝ๋ ๋ฉ๋ชจ๋ฆฌ ์์ญ (Heap, Data, Stack, Code)๋ฅผ OS๋ก ๋ถํฐ ํ ๋น๋ฐ๊ณ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ก์ธ์ค๋น ์ต์ 1๊ฐ์ ์ค๋ ๋๋ฅผ(๋ฉ์ธ์ค๋ ๋) ํ ๋น ๋ฐ์ต๋๋ค. ๊ฐ ํ๋ก์ธ์ค๋ ๋ณ๋์ ์ฃผ์๊ณต๊ฐ์์ ์คํ๋๊ณ , ํ ํ๋ก์ธ์ค๋ ๋ค๋ฅธ ํ๋ก์ธ์ค์ ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํ๊ธฐ ์ํด์๋ ๋ณ๋์ ํต์ ์ด ํ์ํฉ๋๋ค.
์ค๋ ๋
๋ ํ ํ๋ก์ธ์ค๋ด์์ ์คํ ์์ญ๋ง ๋ฐ๋ก ํ ๋น๋ฐ๊ณ Code, Heap, Data ์์ญ์ ๊ณต์ ๋ฐ์ ํ๋ก์ธ์ค๊ฐ ํ ๋น๋ฐ์ ์์ ์ ์ํํ๋ค. ์ค๋ ๋๋ ํ ํ๋ก์ธ์ค ๋ด์ ์ฌ๋ฌ๊ฐ๊ฐ ์กด์ฌํ ์ ์๊ณ ๊ฐ์ ํ๋ก์ธ์ค์ ์๋ ์ค๋ ๋๋ค์ ์๋ก ์์์ ๊ณต์ ํ ์ ์๋ค.
๋ฉํฐ-ํ๋ก์ธ์ค
: ํ๋์ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ฌ๋ฌ๊ฐ์ ํ๋ก์ธ์ค๋ก ๊ตฌ์ฑํด์ ๊ฐ ํ๋ก์ธ์ค๊ฐ ํ๋์ ์์
์ ์ฒ๋ฆฌ๋ฉํฐ ํ๋ก์ธ์ค์ ๊ฒฝ์ฐ ํ๋ก์ธ์ค์ ํน์ฑ์ ์๊ฐํ๋ฉด ์ฅ,๋จ์ ์ ํ์ ํ๊ธฐ ์ฝ๋ค.
ํ๋ก์ธ์ค๋ ๋ค๋ฅธ ํ๋ก์ธ์ค์ ๋ณ๋์ ์ฃผ์๊ณต๊ฐ์์ ์คํ๋๋ค
-> ์ฌ๋ฌ๊ฐ์ ์์ ํ๋ก์ธ์ค ์ค ํ๋์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฉด ํด๋น ํ๋ก์ธ์ค๋ง ์ฃฝ๊ณ ๋ค๋ฅธ ํ๋ก์ธ์ค์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.ํ ํ๋ก์ธ์ค์์ ๋ค๋ฅธ ํ๋ก์ธ์ค ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํ๊ธฐ ์ํด์๋ ๋ณ๋์ ํต์ ์ด ํ์ํ๋ค
-> ํ๋ก์ธ์ค๋ผ๋ฆฌ๋ ๋
๋ฆฝ๋ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ ํ ๋น ๋ฐ๊ธฐ ๋๋ฌธ์ Context Switching์ด ๋ฐ์ํ๋ฉด ์บ์ฌ์ ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ชจ๋ ์ง์ฐ๊ณ ์บ์ฌ์ ๋ณด๋ฅผ ๋ค์ ๋ถ๋ฌ์์ผํ๋ค.(์๊ฐ์ ์ ์ํด)
Context Switching
: CPU์์ ์ฌ๋ฌ ํ๋ก์ธ์ค๋ฅผ ๋์๊ฐ๋ฉด์ ์์ ์ ์ฒ๋ฆฌํ๋ ๊ณผ์ , ๋์์ค์ธ ํ๋ก์ธ์ค๊ฐ ํด๋น ํ๋ก์ธ์ค์ ์ํ๋ฅผ ์ ์ฅํ๊ณ ๋๊ธฐํ๊ณ ์๋ ๋ค์ ํ๋ก์ธ์ค๊ฐ ๋์ํ๋ฉด ์ด์ ์ ๋ณด๊ดํ๋ ์ํ๋ฅผ ๋ณต๊ตฌ
๋ฉํฐ-์ค๋ ๋
: ํ๋์ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ฌ๋ฌ ์ค๋ ๋๋ก ๊ตฌ์ฑํ๊ณ ๊ฐ ์ค๋ ๋๊ฐ ํ๋์ ์์
์ ์ฒ๋ฆฌ๋ง์ฐจ๊ฐ์ง๋ก ์ค๋ ๋์ ํน์ฑ์ ๋ง์ถฐ์ ๋ฉํฐ ์ค๋ ๋๋ฅผ ์๊ฐํด๋ณด์
๊ฐ์ ํ๋ก์ธ์ค๋ด ์ค๋ ๋๋ค์ ์์์ ๊ณต์ ํ๋ค
-> ์ค๋ ๋๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๊ณผ์ ์ด ๊ฐ๋จํ๋ฏ๋ก ์์คํ
์์ ์๋ชจ๊ฐ ์ค์ด๋ ๋ค.์ด๋ ๊ฒ๋ง ๋ณด๋ฉด ๋ฉํฐ-์ค๋ ๋ ๋ฐฉ์์ด ๋ฉํฐ-ํ๋ก์ธ์ค ๋ฐฉ์๋ณด๋ค ์ข์์ ๋ง ์๋๊ฒ ๊ฐ์ง๋ง ๋ค์ํ ๋ฌธ์ ์ ๋ค์ด ์กด์ฌํ๋ค.
๋ฐ๋ผ์, ๋ฉํฐ-์ค๋ ๋๋ฅผ ์ํด์๋ ์ฃผ์ ๊น์ ์ค๊ณ๊ฐ ํ์ํ๋ค
์ฒ์์ ์บ์ฑ์ ํ๋ค๋ ์๋ฏธ๋ฅผ ์บ์ฑํ ์ ๋ณด๋ฅผ ์บ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฐ๋ค๊ณ ๋ง ์๊ฐํ๋๋ฐ ์บ์ ๋ฉ๋ชจ๋ฆฌ๋ ์ด๋ฐ ์ญํ ์ ํ๋ ๋ฌผ๋ฆฌ์ ์ฅ์น๋ฅผ ์๋ฏธํ๋ ๊ฒ์ด๊ณ ์บ์ฑ ์์ฒด๋ ๋ฉ๋ชจ๋ฆฌ(๋ฉ๋ชจ๋ฆฌ ์บ์)๋ ๋์คํฌ(๋์คํฌ ์บ์) ์ด๋์์๋ ํ ์ ์๋ค.
HTTP ํต์ ์์ ์บ์๋ฅผ ์ด์ฉํ๋ ๊ฒฝ์ฐ๋ฅผ ํ์ธํด๋ณด์
ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์๊ฒ ์ฉ๋์ด ํฐ ์ด๋ฏธ์ง๋ฅผ ์์ฒญํ๋ค. 1๋ถํ์ ํด๋ผ์ด์ธํธ๊ฐ ๋์ผํ ์ด๋ฏธ์ง๋ฅผ ์์ฒญํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น? ์๋ฒ๋ ๋ค์ ๋์ผํ ์ด๋ฏธ์ง๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต๊ฐ์ผ๋ก ์ค๋ค.
์บ์๋ฅผ ํตํด์ ์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
์ฒ์ ์๋ฒ์์ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต์ ์ค ๋ ์บ์ ์ ํจ ์๊ฐ์ ์ฃผ๋ฉด ํด๋น ์๊ฐ์ด ์ด๊ณผ๋๊ธฐ ์ ๊น์ง๋ ๋คํธ์ํฌ ํต์ ์์ด ์บ์์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ ํจ์๊ฐ์ด ๋๋ ๊ฒฝ์ฐ์๋ ๋ค์ ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ์ด๋ฏธ์ง๋ฅผ ์์ฒญํ๋ค.
์ฌ๊ธฐ์ ์ฑ๋ฅ์ ๋ ์ฌ๋ฆด ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น? ์บ์ ์ ํจ์๊ฐ์ด ์ด๊ณผ๋์ง๋ง ์๋ฒ์์ ๋ฆฌ์์ค๊ฐ ๋ณ๊ฒฝ๋์ง ์์์ ๊ฒฝ์ฐ์๋ ์ฌ์ ํ ์๋ฒ๋ก ๋ถํฐ ์ด๋ฏธ์ง๋ฅผ ๋ค์ ๋ค์ด ๋ฐ์ ํ์๊ฐ ์์ง ์์๊น?
โ ์กฐ๊ฑด๋ถ ์์ฒญ์ ํตํด์ ํด๊ฒฐ
์บ์ ์ ํจ์๊ฐ์ด ๋๋์ ์๋ฒ์ ๋ค์ ์์ฒญ์ ํ๋ฉด
์ฐ๋ฆฌ๋ ํ์์ ๊ฒฝ์ฐ์๋ ์ฌ์ ํ ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด์ ๋คํธ์ํฌ ํต์ ์ ์ค์ด๊ณ ์ถ๋ค. ๊ทธ๋ผ ์บ์์ ์ ์ฅ๋ ๋ฐ์ดํฐ์ ์๋ฒ์ ๋ฐ์ดํฐ๊ฐ ๊ฐ๋ค๋ ๊ฒ์ ์ด๋ป๊ฒ ํ์ธ ํ ์ ์์๊น?
์ฒ์ ์๋ฒ์์ ํด๋ผ์ด์ธํธ์๊ฒ ์บ์ ์ ํจ๊ธฐ๊ฐ๊ณผ ๋ฐ์ดํฐ๊ฐ ๋ง์ง๋ง์ผ๋ก ์์ ๋ ์๊ฐ ๊ฐ์ ํจ๊ป ์ค๋ค.
์ด์ ์บ์ ์ ํจ์๊ฐ์ด ๋๋ ์์ ์ ํด๋ผ์ด์ธํธ๋ ์๋ฒ์๊ฒ ๋ฐ์ดํฐ๊ฐ ๋ง์ง๋ง์ผ๋ก ์์ ๋ ์๊ฐ์ ํค๋์ ๋ฃ์ด์ ๊ฐ์ด ์์ฒญํ๋ค.
๊ทธ๋ผ ์๋ฒ์์ ํด๋น ๋ฆฌ์์ค์ ๋ณ๊ฒฝ ์์ ๊ณผ ์ด๋ฅผ ๋น๊ตํด์ ๋ฆฌ์์ค์ ๋ณ๊ฒฝ์ด ์์๋ค๋ฉด ๋ค์ ์๋ต์ผ๋ก ํด๋น ๋ฆฌ์์ค๋ฅผ ๋๊ฒจ์ฃผ๊ณ ๋ณ๊ฒฝ์ด ์๋ ๊ฒฝ์ฐ์๋ 304 ์ํ์ฝ๋๋ฅผ ํตํด์ ํด๋ผ์ด์ธํธ์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฌ์ฉ ํด๋ ๋จ์ ์๋ ค์ฃผ๊ณ ์บ์ ์ ํจ๊ธฐ๊ฐ์ ๊ฐฑ์ ์์ผ์ค๋ค.
์ด๋ ๋ฌผ๋ก ์์ฒญ-์๋ต ๊ณผ์ ์ด ์๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ ํต์ ์ด ์๋๊ฒ์ ์๋์ง๋ง HTTP ๋ฉ์์ง์ ๊ธฐ์กด์ ๋ค์ด ๋ฐ๋ ๋ฐ์ดํฐ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ฉ๋์ด ๋งค์ฐ ์์์ง๋ค.
์์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ๋ง์ง๋ง์ผ๋ก ์์ ๋ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ ๋ฌด๋ฅผ ํ์ ํ๋๋ฐ ์ค์ ๋ฐ์ดํฐ๋ ๋ณ๊ฒฝ๋์ง ์์๋๋ฐ ์์ ๋ ์๊ฐ๋ง ๋ฐ๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ค์ด๋ฐ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๋๋ ์ค์ ๋ก ์ฃผ์๊ฐ์ ์ ๋ณด๋ง ๋ฐ๋๊ณ ํฌ๊ฒ ์ํฅ์ด ์๋ ๋ณ๊ฒฝ๋ง ์๋ ๊ฒฝ์ฐ๋ ๊ธฐ์กด ์บ์์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ฌํ์ฉํ์ง ๋ชปํ๋ค.
์ด๋ฐ ๋ฌธ์ ์ ๋ค์ ํด๊ฒฐํ๊ธฐ ์ํด์ Last-Modified
๊ฐ ์๋ ETag
๋ฅผ ์ฌ์ฉํ๋ค.
Entity-Tag๋ ๋ฐ์ดํฐ์ ๋งค์นญ ๋๋ ์ด๋ฆ์ผ๋ก ํ์ผ์ ๊ธฐ์ค์ผ๋ก ํด์๋ฅผ ์์ฑํ๋ค. (๋ฌผ๋ก ์์์๋ก ์ค์ ํ ์๋ ์์)
์ด์ ์บ์ ์ ํจ์๊ฐ์ด ์ด๊ณผ๋ ๊ฒฝ์ฐ ๋ณ๊ฒฝ๋ ์๊ฐ ๊ธฐ์ค์ด ์๋ ETag๊ฐ์ ๊ธฐ์ค์ผ๋ก ์บ์ ๋ฐ์ดํฐ๊ฐ ์๋ฒ์ ๋ฐ์ดํฐ๊ฐ ๋์ผํ์ง๋ฅผ ํ๋จํ ์ ์๋ค.
HTTP 1.0 ํ์ ํธํ์ ์ํด์ ์ฌ์ฉ
// ํ์คํ ์บ์ ๋ฌดํจํ
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
์บ์ ๋ง๋ฃ์ผ ์ง์ (๋ ์ง), ์ ์ฐํ Cache-Control: max-age(์ด๋จ์) ์ฌ์ฉ๊ถ์ฅ, ๋์ ํจ๊ป ์ฌ์ฉํ๋ฉด Expires๋ฌด์
IP ํ๋กํ ์ฝ๋ง์ ์ด์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ์ก,์์ ํ๋ ๊ณผ์ ์ ๊ฐ๋จํ๊ฒ ์๊ฐํ๋ฉด ์ฌ์ฉ์๊ฐ ์จ๋ผ์ธ ์ผํ๋ชฐ์์ ๋ฌผ๊ฑด์ ์ฃผ๋ฌธํ๋ ๊ฒ๊ณผ ์ ์ฌํฉ๋๋ค. ๋จ์ํ ์ฃผ์๊ฐ์ ์ ๋ ฅํด ๋ฌผ๊ฑด์ ์ฃผ๋ฌธํ๋ฉด ์๋ฒ์์ ํด๋น ์ฃผ๋ฌธ์ ๋ฐ๊ณ ์ฌ์ฉ์์ ์ฃผ์๋ก ์ฌ์ฉ์๊ฐ ์์ฒญํ ์๋ฃ๋ฅผ ๋ณด๋ด์ฃผ๋ ํํ์ ๋๋ค.
์ด๋ฌํ IP ํ๋กํ ์ฝ์๋ ๋ช ํํ ํ๊ณ๊ฐ ์กด์ฌํฉ๋๋ค.
ํด๋ผ์ด์ธํธ โ ์๋ฒ
, ์๋ฒ โ ํด๋ผ์ด์ธํธ
ํํ๋ก ํจํท์ ๋ณด๋ผ ๋, ๋์ ์๋ฒ๊ฐ ํจํท์ ๋ฐ์ ์ ์๋ ์ํ์ธ์ง ํ์ธํ์ง ์์ต๋๋ค. ์ฆ, IP ํ๋กํ ์ฝ์ ํตํ ๋ฐ์ดํฐ ํต์ ์ ์ฐ๊ฒฐ ์ํ๋ฅผ ํ์ธํ์ง ์๊ณ ์ผ๋ฐฉ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ ๋๋ฌธ์ ํจํท์ ๋ฐ์ง ๋ชปํ๋ ์ํฉ์ด ๋ฐ์ํฉ๋๋ค
ํ์ฌ ๊ทธ๋ฆผ์์๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ์ฌ์ด์ ๋จ์ํ ํ๋์ ๋ฐ์ค๋ก ํํ๋์ด ์์ง๋ง ์ค์ ์ธํฐ๋ท์์๋ ์๋ง์ ๋ ธ๋๋ฅผ ๊ฑฐ์ณ์ ํจํท์ด ์ ๋ฌ๋ฉ๋๋ค. ์ฌ๊ธฐ์ ๋ฐ์ํ๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค
์ ๋ง์ ๋ ธ๋๋ฅผ ๊ฑฐ์น๋ฉด์ ์๋ฒ๋ก ๋ณด๋ธ ํจํท์ด ์ ์ค๋ ์ ์์ต๋๋ค. (์ฐ๋ฆฌ๊ฐ ์ฃผ๋ฌธํ ๋ฌผ๊ฑด์ด ๊ณค์ง์ ๋ฒ๋ฎค๋ค ๋ฌผ๋ฅ์ผํฐ์์ ๊ธธ์ ์๋ ๊ฒ์ฒ๋ผ) ํ์ง๋ง ์๋ฒ๋ ํด๋ผ์ด์ธํธ์์ ์ด๋ค ํจํท์ ๋ณด๋๋์ง์ ๋ํ ์ ๋ณด๊ฐ ์ ํ ์๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ๋ณด๋ธ ํจํท์ด ์ ์ค๋์๋ค๋ ์ฌ์ค ์กฐ์ฐจ ํ์ธํ ์ ์์ต๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ฐ๋ฆฌ๊ฐ ํฐ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ๋ณด๋ด๋ ๊ฒฝ์ฐ, ํ ๋ฒ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ ๋ณด๋ค๋ ๋ฐ์ดํฐ๋ฅผ ์๋ผ์ ๋ณด๋ด๊ฒ ๋๊ณ , ๊ทธ๋ผ ๋ฐ์ดํฐ์ ์์๊ฐ ์ค์ํด์ง๋๋ค. ํ์ง๋ง ํด๋ผ์ด์ธํธ โ ์๋ฒ
๋ก ํตํ๋ ๋
ธ๋๋ ํ๋๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ๋จผ์ ๋ณด๋ธ ๋ฐ์ดํฐ๋ณด๋ค ์ดํ์ ๋ณด๋ธ ๋ฐ์ดํฐ๊ฐ ๋จผ์ ๋์ฐฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํฉ๋๋ค.
ํ์ง๋ง ์๋ฒ๋ ์ฌ๋ฌ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ์ฌ๊ฑฐ๋ผ๋ ์ฌ์ค์กฐ์ฐจ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์ํฉ์ ํด๊ฒฐํ ์ ์์ต๋๋ค
์์ ์ดํด๋ณธ IP์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด์ ํ์ํ๊ฒ TCP์ ๋๋ค. ๊ทธ๋ผ ์์ ๋ฌธ์ ๋ค์ TCP๋ฅผ ์ด์ฉํ๋ฉด ์ด๋ป๊ฒ ํด๊ฒฐ๋๋์ง ํ์ธํด๋ด ์๋ค.
![๋ชจ๋ ๊ฐ๋ฐ์๋ฅผ ์ํ HTTP ์น ๊ธฐ๋ณธ ์ง์ - 1๊ฐ ๊ฐ์์๋ฃ]
์ธํฐ๋ท ํ๋กํ ์ฝ 4๊ณ์ธต์ ํ์ธํด๋ด ์๋ค.
์ฐ์ , ์์ ์ดํด๋ณธ Internet Protocol์ ์ฌ์ฉํ์ง๋ง TCP๊ฐ ์ถ๊ฐ๋๊ฒ์ ํ์ธ ํ ์ ์์ต๋๋ค. ๊ธฐ์กด์ IP ํจํท์ TCP๋ผ๋ ์์๋ก ํฌ์ฅํด์ ์๋ฒ๋ก ๋ณด๋ด๋ ํํ์ ๋๋ค.
IP์์์ ๋น์ฐ๊ฒฐ์ฑ์ ํด๊ฒฐํ ์ ์๋ ํน์ง์ ๋๋ค.
TCP๋ ์๋ฒ-ํด๋ผ์ด์ธํธ
๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๊ธฐ ์ ์ ๋จผ์ ์๋ก ์ฐ๊ฒฐ๋์ด ์๋์ง๋ฅผ ๋จผ์ ํ์ธํฉ๋๋ค.
์ฐ๊ฒฐ์์ ์์ :
3 way handshak
์ฐ๊ฒฐ์ข ๋ฃ ์์ :4 way handshake
(#23, ๊ด๋ จ ์ด์)
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ์ด ์ ์์ ์ด์ง ์์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ง ์์ต๋๋ค.
IP์์์ ํจํท ์ ์ค์ ํด๊ฒฐํ ์ ์๋ ํน์ง์ ๋๋ค.
์๋ฒ-ํด๋ผ์ด์ธํธ
์ฐ๊ฒฐ์ด ๋์ด ์๋ ์ํ์์ ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ์ด๋ค ์ ๋ณด๋ฅผ ๋ณด๋ด๊ณ ์๋ฒ๊ฐ ํด๋น ํจํท์ ๋ฐ์ผ๋ฉด ํด๋ผ์ด์ธํธ๋ก ํจํท์ ๋ฐ์๋ค๋ ack ๋ณด๋ด์ค๋๋ค.
๋ง์ฝ ํด๋ผ์ด์ธํธ๊ฐ ํจํท์ ๋ณด๋ด๊ณ ์ผ์ ์๊ฐ ์๋ฒ๋ก ๋ถํฐ ack์ ๋ฐ์ง ๋ชปํ๋ ๊ฒฝ์ฐ์๋ ๋ค์ ํจํท์ ๋ณด๋ด๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ ์ ๋ฌ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
IP์์์ ํจํท ์ ๋ฌ ์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ํน์ง์ ๋๋ค.
TCP๋ฅผ ํตํด ํจํท์ ์์๋ฅผ ๋ฏธ๋ฆฌ ์๊ณ ์๊ธฐ ๋๋ฌธ์ ์ฌ๋ฐ๋ฅธ ์์๋ก ํจํท์ด ์ค์ง ์์ ๊ฒฝ์ฐ, ๋ค์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๊ฑฐ๋ ๋ด๋ถ์ ์ผ๋ก ์กฐํฉํด์ ์ฌ๋ฐ๋ฅธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค.
ํด๋ผ์ด์ธํธ์์ ์ฌ๋ฌ ์๋ฒ์ ํต์ ํ๊ณ ์๋ ๊ฒฝ์ฐ, ๊ฐ์ IP์ ์ฌ๋ฌ ๋ฐ์ดํฐ๊ฐ ์ค๋๋ฐ ์ด๋ป๊ฒ ๊ตฌ๋ถํ ๊ฒ์ธ๊ฐ?
ํจํท์ ์กด์ฌํ๋ port๋ฅผ ํตํด์ ํ๋ก์ธ์ค๋ฅผ ๊ตฌ๋ถํ ์ ์๋ค.
์ฐ๋ฆฌ๊ฐ www.google.com์ ์ ๋ ฅํ๋ฉด DNS ์๋ฒ์์ IP์ฃผ์๋ก ๋ณํํด์ ์น์ฌ์ดํธ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์๋๊ฒ ์ฒ๋ผ IP ์ ํ๋ฒํธ๋ถ๋ก ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
์ด๋ฌํ ํน์ง ๋๋ฌธ์ ์ผ๋ฐ์ ์ผ๋ก IP์ฃผ์ ๋ณด๋ค๋ DNS์๋ฒ์ IP๋ฅผ ๋ฑ๋กํ๊ณ ๋๋ฉ์ธ ์ด๋ฆ์ ํตํด์ ํต์ ํฉ๋๋ค.
HTTP/1.1 200 OK // ์์๋ผ์ธ
Content-Type: text/html;charset=UTF-8 // ์ํฐ๋ ํค๋
Content-Length: 3423
<html> // ์ํฐ๋ ๋ณธ๋ฌธ
...
</html>
HTTP ๋ฉ์์ง ๊ตฌ์กฐ๋ฅผ ๊ณต๋ถํ๋ฉด์ ์ดํด๋ดค๋ฏ์ด ์์๋ผ์ธ, ํค๋, ๊ณต๋ฐฑ, ๋ณธ๋ฌธ
๊ตฌ์กฐ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
์ํฐ๋ ํค๋๋ ๋ฉ์์ง ๋ณธ๋ฌธ์ ํตํด ์ ๋ฌํ๋ ๋ฐ์ดํฐ๋ฅผ ํด์ํ ์ ์๋ ์ ๋ณด๋ค์ ์ ๊ณตํ๋ค.
2014๋
์ดํ๋ก HTTP ํ์ค์ด ๋ฐ๋๋ฉด์ ์์ ๊ตฌ์กฐ์์ Entity๊ฐ Representation
์ผ๋ก ๋์ฒด๋๋ ํฐ ์ฐจ์ด์ ์ด ๋ฐ์ํ๋ค. (์์ ํ 1:1 ๋์๋๋ ๊ฐ๋
์ ์๋)
์ฌ์ค ๋์ ์ ํํ ์ฐจ์ด์ ์ ํ์ ํ ์ ๋๋ก ๊น๊ฒ ๊ณต๋ถํ์ง๋ ๋ชปํ์ง๋ง ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํด๋ณด์๋ฉด
Entity๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ ์ฃผ๊ณ ๋ฐ๋ ๋ฐ์ดํฐ์ ๋ณธ์ง์ ๋ ๊ฐ๊น์ด ๋๋์ธ๋ฐ ์ฌ์ค ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ํํํด์ ์ฃผ๊ณ ๋ฐ์์ง ์๋ก ์ฝ์ํ๊ณ ๊ฐ์์ ๋ด๋ถ์ ์ ๋ก์ง์ ํตํด ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ ์๋ฒ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ๋ด๋ถ์ ์ผ๋ก ๊ฐ์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ด๋ค ํ์์ผ๋ก ์ ์ฅํ๊ณ ์๊ฑด json ํ์ ์ผ๋ก ์ฃผ๊ณ ๋ฐ๋๋ค๋ ์ฝ์์ ํ๋ค. ์ ๋์ ๋๋์ผ๋ก ๋ฐ์๋ค์๋๋ฐ ํด๋น ๋ด์ฉ์ ์ข ๋ ๊ณต๋ถํ๋ค๊ฐ ์๊ฒ๋๋ ๋ด์ฉ์ด ์์ผ๋ฉด ์ ๋ฆฌํด์ผ๊ฒ ๋ค.
HTTP/1.1 200 OK // ์์๋ผ์ธ
Content-Type: text/html;charset=UTF-8 // ํํ ํค๋
Content-Length: 3423
<html> // ํํ ๋ฐ์ดํฐ
...
</html>
HTTP ๋ฉ์๋ ํ์ฉ์ ์ ๋ฆฌํ๋ฉด์ ์ ๋ฆฌํ๋ ๋ถ๋ถ์ ๊ฐ๋จํ๊ฒ ๋ค์ ์ง๊ณ ๋์ด๊ฐ๋ณด์.
Content-Type์ HTTP ์์ฒญ ๋๋ ์๋ต์ ๋ณธ๋ฌธ(content)์ ํํ ๋ฐ์ดํฐ ํ์์ ์ค๋ช ํ๊ณ MIME ํ์ ์ ์ด์ฉํด ๋ฏธ๋์ด ํ์ ์ ๋ช ์ํ๋ค.
ํํ ๋ฐ์ดํฐ๋ฅผ ์์ถํ๊ธฐ ์ํด์ ์ฌ์ฉ
๋ฐ์ดํฐ๋ฅผ ์ฝ๋ ์ชฝ์์ ์ธ์ฝ๋ฉ ํค๋์ ์ ๋ณด๋ฅผ ์ด์ฉํด์ ์์ถ ํด์ [ex) gzip, deflate, identity]
ํํ ๋ฐ์ดํฐ์ ์์ฐ์ธ์ด๋ฅผ ํํ [ex) ko, en, en-US]
๋ฐ์ดํธ ๋จ์, ์ ์ก ์ฝ๋ฉ์ ์ฌ์ฉํ๋ฉด Cotent-Length๋ฅผ ์ฌ์ฉํ๋ฉด ์๋จ โ ์กฐ๊ธ๋ง ๋ด๋ฆฌ์๋ฉด ํด๋น ๋ด์ฉ ์ ๋ฆฌ
ํด๋ผ์ด์ธํธ๊ฐ ์ ํธํ๋ ํํ ์์ฒญ
์ด๋ผ๋ ๋ฌธ์ฅ๋ง ๋ณด๋ฉด ์ ํํ๊ฒ ์ดํดํ๊ธฐ ํ๋ค ์ ์๋๋ฐ ์์ ๋ฅผ ๋ค์ด์ ๊ฐ์ด ์ดํดํด๋ณด์
์์ ๊ทธ๋ฆผ์ฒ๋ผ ๋ค๊ตญ์ด๋ฅผ ์ง์ํ๋ ์๋ฒ์์ ํด๋ผ์ด์ธํธ์ ์๋ต์ ์ค ๋ ์์ฒญ์ ํ์ ํค๋๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ์ผ๋ก ์ง์ํ๋ ์์ด๋ก ์๋ต์ ๋ฐ๊ฒ๋๋ค.
Accept: text/*, text/plain, text/plain;format=flowed, **/*
1)* text/plain;format=flowed
2) text/plain
3) text/*
4) **/**
q๊ฐ์ ๋ฐ๋ก ๋ช
์ํ์ง ์์๋ ๊ตฌ์ฒด์ ์ธ ๊ฐ์ด ์ฐ์ ์์๋ฅผ ๊ฐ์ง๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ๋ก ๋ฐ์ ์๋ต ๊ฐ์ ๋ ์ ํธํ๋ ํ์์ ์ง์ ํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์์ฒญ์์๋ง ์ฌ์ฉ๋๋ค.
์ฟ ํค๋ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋ด๋ ์์ ๋ฐ์ดํฐ ์กฐ๊ฐ
์ฟ ํค์ ๋ํด ์ดํดํ๊ธฐ ์ํด์๋ ์ฟ ํค๋ฅผ ์ ์ฌ์ฉํ๋์ง๋ฅผ ๋จผ์ ์ดํดํ ํ์๊ฐ ์๋ค.
HTTP ํน์ฑ์ ๋ค์ ๋ค์ฌ๋ค ๋ณด๋ฉด
๋ฌด์ํ(Stateless), ์๋ฒ๋ ํด๋ผ์ด์ธํธ์ ์ํ๋ฅผ ์ ์ฅํ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ๋ ์ด์ ์ํ์ ๋ํ ์ ๋ณด๋ฅผ ํจ๊ป ์๋ฒ์ ๋ณด๋ด์ผ ํ๋ ๋ถํธํจ์ด ์์์ง๋ง ์ค์ผ์ผ ์์์ด ์ฌ์์ง๋ค๊ณ ์ ๋ฆฌํ๋ฐ ์๋ค.
๊ทธ๋ผ ์ด์ ์ํ์ ๋ํ ์ ๋ณด๋ฅผ ์๋ฒ์ ๋๊ธฐ๋ ๋ฐฉ๋ฒ์๋ ๋ฌด์์ด ์์๊น?
๊ฐ๋จํ๊ฒ ๋ชจ๋ ์์ฒญ์ ์ด์ ์ํ ์ ๋ณด๋ฅผ ๋๊ธธ ์ ์๋ค. ํ์ง๋ง ์น ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ซ์ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผ ํ ๊น?
์ด๋ฐ ๋ฌธ์ ๋ค์ ๊ฐ๋จํ๊ฒ ํด๊ฒฐํ๊ธฐ ์ํด ๋์จ๊ฒ์ด ์ฟ ํค์ด๋ค.
๋จผ์ ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ์ฟ ํค๋ฅผ ์ ๋ฌํ๋ค.
์ดํ ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ผ ๋, ์ฟ ํค์ ์๋ ์ ๋ณด๋ฅผ ํฌํจํด์ ๋ณด๋ธ๋ค.
๊ทธ๋ผ ์น ๋ธ๋ผ์ฐ์ ๊ฐ ๋ซํ๋ฉด ์ฟ ํค๋ ์ด๋ป๊ฒ ๋ ๊น? ๋ ํด๋ผ์ด์ธํธ์ ์ ์ฅ๋ ์ฟ ํค๋ ์ธ์ ๊น์ง ์ ์ฅ๋๋๊ฑธ๊น?
์๋ฒ์์ ์ฒ์ ํด๋ผ์ด์ธํธ์๊ฒ ์ฟ ํค๋ฅผ ๋ณด๋ด์ค ๋ ์ฟ ํค์ ๋ง๋ฃ์ผ์ ์ค์ ํ ์ ์๋ค.
Set-Cookie: expire=Sat,26-Dec-2020 04:39:23 GMT // ๋ง๋ฃ์ผ์ด ๋๋ฉด ์ญ์
Set-Cookie: max-age=3600 // 3600์ด ์ดํ ์ญ์
์๋ฒ์์ ๋ง๋ฃ์ผ์ ์ค์ ํ๋ค๊ณ ํด์ ํด๋ผ์ด์ธํธ์์ ์ฟ ํค ๋ง๋ฃ์ผ์ด ๋๋ฉด ์ด๋ป๊ฒ ์๋์ผ๋ก ์ญ์ ํ ๊น?๋ผ๋ ์๋ฌธ์ด ๋ค์ด์ ๊ฒ์ํ๋๋ฐ ๋ธ๋ผ์ฐ์ ์ ์ฟ ํค ์ ์ฑ ์ ๋ฐ๋ผ์ ๋ค๋ฅผ ์ ์๋ค๊ณ ํ๋ค.
ํด๋ผ์ด์ธํธ์ ์ ์ฅ๋ ์ฟ ํค ์ ๋ณด๋ ํญ์ ์๋ฒ์ ์ ์ก๋๋๋ฐ ํ์ฌ ์น ์ฌ์ดํธ๊ฐ ์๋ ๋ค๋ฅธ ์ฌ์ดํธ์์ ๋ฐ์ ์ ์ฅํ ์ฟ ํค๋ ํจ๊ป ์ ์ก๋๋๊ฑธ๊น? (๋น์ฐํ ๊ทธ๋ฌ๋ฉด ์๋๊ฒ ์ฃ ?)
domain=example.org // ๋ฌธ์ ๊ธฐ์ค ๋๋ฉ์ธ, ์๋ธ ๋๋ฉ์ธ ํฌํจ -> dev.example.org
domain(empty) // ํด๋น ๋๋ฉ์ธ์์๋ง ์ ๊ทผ ๊ฐ๋ฅ
๋๋ฉ์ธ๊ฐ์ ์ ์ด์ฃผ์ง ์๋๋ผ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํด๋น ์ฟ ํค๋ฅผ ๋ณด๋ด์ค ๋๋ฉ์ธ์ ์์ฒญ์ ๋ณด๋ผ ๋๋ง ํด๋น ์ฟ ํค๋ฅผ ์ฌ์ฉํ๋ค.
ํญ์ ์๋ฒ์ ์ ์ก๋๋ ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ๋ณด์์ ์ทจ์ฝํ๋ค. ์ต์ํ์ ์ ๋ณด๋ง ๋ณด๋ด์ผ ํ๋ค.
๊ด๋ จ๋ ๋ด์ฉ์ ์๋์ง๋ง ํ์ตํ๋ค ์ด ์์์ด ๋๊ฒ ์ ์ฉํ๋๊ฑฐ ๊ฐ์์ ๊ณต์ ํด๋๋ฆฝ๋๋ค.
๋ง์ดํฌ๋กํ๋ก์ธ์(MPU)
: ์ปดํจํฐ์ ํต์ฌ ๊ธฐ๋ฅ์ธ ๊ธฐ๊ณ์ด๋ฅผ ํด์ํ๊ณ , ์ฐ์ฐ์ ์ํํ๋ ๊ธฐ๋ฅ๋ง ๊ฐ์ง๊ณ ์๋ ํ๋ก์ธ์(์ฃผ๋ณ์ฅ์น๊ฐ ์์ด์ผ ๋์/ ์์ฆ์๋ CPU์ ๋์ผํ ์๋ฏธ๋ก ์ฌ์ฉํ๊ธฐ๋ ํฉ๋๋ค.)
๊ทธ๋ผ ์์ ์ ์๋ฅผ ์ข ํ์ด์ ์ค๋ช
ํด๋ณด๋ฉด CPU๊ฐ ์ผ์ ํ๊ณ ์๋๋ฐ ๋ค๋ฅธ ์ฅ์น์์ ์์ธ์ํฉ์ด ๋ฐ์ํด ์์
์ด ํ์ํ ์๊ฐ ์ธํฐ๋ฝํธ
๋ฅผ ๋ฐ์ ์์ผ์ CPU์๊ฒ ๊ทธ ์ผ์ ์ํจ๋ค. ๊ทธ ์ ๋ ์๋ฏธ๋ก ์๊ฐํ ์ ์์ต๋๋ค.
๊ทธ๋ผ ์ ๊ตณ์ด ์ธํฐ๋ฝํธ
๋ฅผ ๋ฐ์์์ผ์ CPUํํ
์ผ์ ์ํฌ๊น์? -> CPU์ด ์ฐ์ฐ์๋๊ฐ ๋ ๋น ๋ฆ
๋๋ค
ํ๋์จ์ด ์ธํฐ๋ฝํธ
: ํค๋ณด๋, ๋ง์ฐ์ค์ ๊ฐ์ ํ๋์จ์ด๊ฐ ๋ฐ์์ํค๋ ์ธํฐ๋ฝํธ์ํํธ์จ์ด ์ธํฐ๋ฝํธ
: ํ๋ก๊ทธ๋จ ์ค๋ฅ๋ก ์ธํด ์์ธ์ํฉ์ด ๋ฐ์ ํ๊ฑฐ๋ ํ๋ก๊ทธ๋จ์ด ์ปค๋ํจ์ ์ฌ์ฉ์ ์ํด ํธ์ถํ๋ System call ์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ
์ธํฐ๋ฝํธ๊ฐ ๋ฐ์ํ๋ฉด ์ธํฐ๋ฝํธ ์๋น์ค๋ฅผ ์คํ์์ผ์ผ ํ๊ธฐ ๋๋ฌธ์ ํ์ฌ CPU์์ ์์
์ค์ด์๋ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค. ์ด๋ PCB
๋ฅผ ํตํด์ ํ์ฌ ์คํ์ค์ด๋ ์ฝ๋์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์, ๋ ์ง์คํฐ๊ฐ, ํ๋์จ์ด ์ํ๋ฅผ ์ ์ฅํฉ๋๋ค.
์ด์์ฒด์ ๋ ๋ฏธ๋ฆฌ ์ธํฐ๋ฝํธ ๋ฒกํฐ
๋ฅผ ๊ฐ์ง๊ณ ์๋๋ฐ ์ธํฐ๋ฝํธ ๋ฒกํฐ๋ฅผ ๋ฐ๋ผ๊ฐ๋ฉด ์ค์ ์ฒ๋ฆฌํด์ผ ํ ์ฝ๋๋ ์ธํฐ๋ฝํธ ํธ๋ค๋ฌ๋ผ๊ณ ๋ถ๋ฆฌ๋ ๊ณณ์ ์ ์ ๋์ด ์๊ณ ์ด๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
ํ๋ถ์์ ๊ธฐ์ต์ ๋ ์ฌ๋ ค๋ณด๋ฉด ์ธํฐ๋ฝํธ์ ํด๋นํ๋ ์ฃผ์๊ฐ์ด ์ด๋ฏธ ์ค๊ณํ ๋ ๋ฏธ๋ฆฌ ์ ํด์ ธ์์๋๊ฑธ๋ก ๊ธฐ์ตํ๋๋ฐ ๊ทธ ๋ถ๋ถ์ ์ธํฐ๋ฝํธ ๋ฒกํฐ๋ผ๊ณ ํ๋๊ฑฐ ๊ฐ์ต๋๋ค
์ธํฐ๋ฝํธ์์ ์๊ตฌํ๋ ์์ ์ ๋ชจ๋ ์๋ฃํ๋ฉด ์๋ CPU๊ฐ ํ๋ ์์ ์ ์ด์ด์ ํ๋ ๊ณผ์ ์ ์๋ฏธํฉ๋๋ค.(์๊น PCB์ ์ ์ฅํด๋จ์ผ๋๊น ๋์์ฌ ์ ์์ต๋๋ค.)
์์คํ ์ฝ์ ๋ํ ๋ถ๋ถ์ ๊ณต๋ถํ๋ฉด ์ธํฐ๋ฝํธ์์ ์ฐจ์ด์ ์ ๋ํด์ ์ถ๊ฐ์ ์ผ๋ก ์ ๋ฆฌํ๋๋ก ํ๊ฒ ์ต๋๋ค.
ํ์ฌ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐ๋ผ Media List๋ฅผ ๋ฐ์์ค๊ณ ๊ฐ Cell์ ํด๋ฆญํ๋ฉด DetailView๋ก Push ๋๊ณ ์๋ ํํ์ ์ฑ์ด๋ค.
struct MediaListView: View {
@StateObject
var viewModel: MediaListViewModel
let appContainer: AppContainer
var body: some View {
NavigationView {
ScrollView {
LazyVStack {
ForEach(viewModel.output.medias, id: \.id) { media in
NavigationLink(
destination: {
NavigationLazyView(
MediaDetailView(
viewModel: appContainer.mediaDetailViewModel(media)
)
)
},
label: {
MediaListItemView(media: media)
}
)
}
}
.padding(.top, 20)
.padding([.leading, .trailing], 12)
}
.navigationTitle("TV Search")
.navigationBarTitleDisplayMode(.inline)
.searchable(text: $viewModel.input.searchMediaSub.value)
}
}
}
SwiftUI์ NavigationLink๋ ์ฌ์ฉ์๊ฐ pushํ๊ธฐ ์ด์ ๋ทฐ์์๋ push๋ ๋ทฐ๋ฅผ ๋ฏธ๋ฆฌ loadํ๊ณ ์๋ ๋ฌธ์ ์ (?)์ด ์๋๋ฐ ์ด๋ฅผ ๋ฐฉ์งํ๊ณ ์ push๋๋ ์์ ์์ ๋ทฐ๊ฐ ๊ทธ๋ ค์ง ์ ์๋๋ก NavigaitonLazyView๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค.
ํ์ง๋ง ์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๊ณ , ๊ธฐ์กด์ ๋ฆฌ์คํธ์ ์๋ cell์ ํด๋ฆญํ๋ ์์ ์ ์ฌ์ฉ์๊ฐ ํค๋ณด๋๋ฅผ ํตํด ๋ค๋ฅธ ๊ฐ์ ์ ๋ ฅํ๋ฉด ์๋์ผ๋ก navigation pop์ด ๋๋ ๋ฌธ์ ์ ์ด ์๋ค.
์ ๋ ฅ๊ฐ์ด ๋ณํํจ์ ๋ฐ๋ผ์ ๊ธฐ์กด์ list๊ฐ ๊ฐ์ง๊ณ ์๋ cell์ด ์ฌ๋ผ์ง๋ฉด์ navigationLink๋ ์ฌ๋ผ์ ธ ๋ฒ๋ฆฐ๊ฒ์ด๋ค!
์ด๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ์ด์ ๋ list์ cell์ด navigationLink๋ผ๋ View๋ก ๋ง๋ค์ด์ก๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ List์ cell Item ์์ฒด๊ฐ navigationLink๊ฐ ๋์ด์๋ ์๋๋ค.
import SwiftUI
struct MediaListView: View {
@StateObject
var viewModel: MediaListViewModel
let appContainer: AppContainer
var body: some View {
NavigationView {
ScrollView {
LazyVStack {
// Empty View
NavigationLink(
isActive: $viewModel.output.isNavigationShow,
destination: {
if let media = viewModel.output.selectedMedia {
NavigationLazyView(
MediaDetailView(
viewModel: appContainer.mediaDetailViewModel(media)
)
)
}
},
label: { }
)
// List Cell Item
ForEach(viewModel.output.medias, id: \.id) { media in
MediaListItemView(media: media)
.wrapToButton { viewModel.action(.navigationTapped(media)) }
}
}
.padding(.top, 20)
.padding([.leading, .trailing], 12)
}
.navigationTitle("TV Search")
.navigationBarTitleDisplayMode(.inline)
.searchable(text: $viewModel.input.searchMediaSub.value)
}
}
}
ํด๊ฒฐ๋ฐฉ๋ฒ์ ๊ฐ๋จํ๋ค. List์ธ๋ถ์ Empty View Label์ ๊ฐ์ง๋ NavigationLink๋ฅผ ๋ง๋ค๊ณ , ํด๋น NavigationLink๋ฅผ ํตํด์ ํ๋ฉด์ ํ์ ํ๋ฉด๋๋ค.
์ฝ๋๊ฐ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ๋ณด์. ๊ธฐ์กด์๋ List์ Item ์์ฒด๊ฐ NavigationLink์์ง๋ง ๋จ์ํ Button ๊ธฐ๋ฅ์ ํ๋ ๋ฒํผ์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
import Combine
import Foundation
final class MediaListViewModel: ViewModelType {
private let searchMediaUsecase: SearchMediaUseCase
struct Input {
var searchMediaSub = BindingSubject<String>(value: "")
fileprivate let searchButtonSub = PassthroughSubject<Void, Never>()
fileprivate let navigationSub = PassthroughSubject<Media, Never>()
}
enum Action {
case searchButtonTapped
case navigationTapped(Media)
}
func action(_ action: Action) {
switch action {
case .searchButtonTapped:
input.searchButtonSub.send()
case .navigationTapped(let media):
input.navigationSub.send(media)
}
}
struct Output {
var medias: [Media] = []
var isNavigationShow: Bool = false
var selectedMedia: Media?
}
var input = Input()
@Published var output = Output()
var cancellables = Set<AnyCancellable>()
init(searchMediaUsecase: SearchMediaUseCase) {
self.searchMediaUsecase = searchMediaUsecase
transform()
}
func transform() {
input.searchMediaSub.subject
.debounce(for: 0.5, scheduler: RunLoop.main)
.flatMap { [weak self] search -> AnyPublisher<MediaPage, Never> in
guard let self = self else {
return Just(MediaPage(page: -1, totalPages: -1, medias: [])).eraseToAnyPublisher()
}
return self.searchMediaUsecase.tvExcute(query: search, page: 1)
.replaceError(with: MediaPage(page: -1, totalPages: -1, medias: []))
.eraseToAnyPublisher()
}
.map { $0.medias }
.sink(receiveValue: { [weak self] in
guard let self = self else { return }
self.output.medias = $0
})
.store(in: &cancellables)
input.navigationSub
.sink(receiveValue: { [weak self] in
guard let self = self else { return }
self.output.selectedMedia = $0
self.output.isNavigationShow = true
})
.store(in: &cancellables)
}
}
๋ทฐ๋ชจ๋ธ์ ํ๋ ์ ํ
์ด์
๋ก์ง์ ํ์ธํด๋ณด๋ฉด, ์ฌ์ฉ์๊ฐ DetailView๋ฅผ ๋ณด๊ธฐ ์ํด์ ๋ฒํผ์ ๋๋ฅด๋ฉด ํด๋น ๋ฒํผ์ ๊ทธ๋ฆด ๋ ์ฌ์ฉ๋์๋ Media๋ฅผ subject๋ฅผ ํตํด ๋๊ฒจ์ฃผ๊ณ navigationLink์ isActive๋ฅผ ๋ณ๊ฒฝํ๋ค.
๋ฌธ์ ๊ฐ ํด๊ฒฐ๋๊ฒ์ ํ์ธํ ์ ์๋ค.
์ ์ฒด ํ๋ก์ ํธ๋ ์ฌ๊ธฐ์ ํ์ธํ ์ ์์ต๋๋ค
WebSocket์ ์ฐ๊ฒฐํด์ ํ ์คํธ ํด๋ณด๋ ์ค ๋ฐ์ํ๋ ๋ฒ๊ทธ(?)๋ฅผ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
์ฐ์ ์ ๋นํธ ์์ผ์ ํฐ๋ฏธ๋์์ ํ ์คํธ ํด๋ณด์
< {
"type":"ticker",
"code":"KRW-BTC",
"opening_price":29398000,
"high_price":29528000,
"low_price":29350000,
"trade_price":29468000,
"prev_closing_price":29420000.00000000,
"acc_trade_price":39734960333.20671000,
"change":"RISE",
"change_price":48000.00000000,
"signed_change_price":48000.00000000,
"change_rate":0.0016315432,
"signed_change_rate":0.0016315432,
"ask_bid":"BID",
"trade_volume":0.01749575,
"acc_trade_volume":1349.62787321,
"trade_date":"20230205",
"trade_time":"103530",
"trade_timestamp":1675593330855,
"acc_ask_volume":647.32427056,
"acc_bid_volume":702.30360265,
"highest_52_week_price":57678000.00000000,
"highest_52_week_date":"2022-03-28",
"lowest_52_week_price":20700000.00000000,
"lowest_52_week_date":"2022-12-30",
"market_state":"ACTIVE",
"is_trading_suspended":false,
"delisting_date":null,
"market_warning":"NONE",
"timestamp":1675593330915,
"acc_trade_price_24h":81916979573.45246000,
"acc_trade_volume_24h":2780.05924818,
"stream_type":"SNAPSHOT"
}>
์์ผ ์ฐ๊ฒฐ์ด ๋๋ฉด ์์ ๊ฐ์ ๊ฐ๋ค์ด ์ค์๊ฐ์ผ๋ก ๋ค์ด์ค๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ํํ์ด์ง์์ ์ค๋ช ํ idle timeOut๋ฅผ ํ ์คํธ ํ๊ธฐ ์ํด์ ์์ผ ์ฐ๊ฒฐ ํ, 120์ด ๊ฐ ๋ฐ์ดํฐ ์ก ์์ ์ด ์๋ ์ํ๋ก ๋๊ธฐ ํด๋ณด์๋ค. (ping, pong x)
์ฝ 120์ด๊ฐ ์ง๋๋ฉด DisConnected ๋๋๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด์ ์์ ์์ผ ์ฐ๊ฒฐ์ Starscream์ ์ด์ฉํด ๊ตฌํํ๊ณ ํ ์คํธ ํด๋ณด์
final class SocketManager {
static let shared = SocketManager()
private var socket: WebSocket?
private init() {
setupWebSocket()
}
deinit {
socket?.delegate = nil
}
private func setupWebSocket() {
let url = URL(string: "wss://api.upbit.com/websocket/v1")!
var request = URLRequest(url: url)
request.timeoutInterval = 5
socket = WebSocket(request: request)
}
func connect() {
socket?.delegate = self
socket?.connect()
}
func disconnect() {
socket?.disconnect()
}
private func sendMessage(_ message: String) {
socket?.write(string: message)
}
private func sendRequest() -> String {
"""
[{"ticket":"\(UUID())"},{"type":"orderbook","codes":["KRW-BTC"]}]
"""
}
}
extension SocketManager: WebSocketDelegate {
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(let headers):
print("websocket is connected: \(headers)")
case .disconnected(let reason, let code):
print("websocket is disconnected: \(reason) with code: \(code)")
case .text(let text):
print("received text: \(text)")
case .binary(let data):
print("Received data: \(data.count)")
case .ping(_):
print("Ping: ")
case .pong(_):
print("Pong: ")
case .viabilityChanged(_):
print("viabilityChanged")
case .reconnectSuggested(_):
print("reconnectSuggested")
case .cancelled:
print("websocket is canclled")
case .error(let error):
print("websocket is error = \(error!)")
}
}
}
ํฐ๋ฏธ๋์์ ๋ณธ ๊ฒฐ๊ณผ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ผ ์ฐ๊ฒฐ์ ํ๊ณ , ํฐ์ผ์ ๋ฉ์์ง๋ก ๋ณด๋ด๋ฉด ๋์ผํ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐ์ ์ ์๋ค.
ํ์ง๋ง idle timeout๋ฅผ ํ ์คํธ ํด๋ณด๋ฉด, ํด๋ผ์ด์ธํธ ๋จ์์ CloseWait ์ํ๋ก ๋ฌดํํ ๋๊ธฐํ๊ณ ์๋ ๋ฌธ์ ์ ์ ๋ฐ๊ฒฌํ๋ค.
TCP ์ข ๋ฃ์์ ์ 4way-handshake ๊ณผ์ ์์ ์ดํด๋ณด๋ฉด,
์๋ฒ๊ฐ ์ฐ๊ฒฐ์ ์ข ๋ฃํ๊ธฐ ๋๋ฌธ์ ์๋ฒ๊ฐ Active close(์ผ์ชฝ), ํด๋ผ์ด์ธํธ๊ฐ Passive close(์ค๋ฅธ์ชฝ)์ด๋ค.
ํ์ฌ ์๋ฒ์์ ์ข ๋ฃํ๊ฒ ๋ค๋ Fin์ ๋ณด๋ด๊ณ ํด๋ผ์ด์ธํธ์์ ํด๋น ์ ํธ๋ฅผ ๋ฐ๊ณ CloseWait ์ํ๋ก ๋ณ๊ฒฝ๋ ์ํ๋ก ๋จธ๋ฌผ๊ณ ์๋ ์ํฉ์ด๋ค. ๊ทธ๋ผ ์๋ฒ๋ ํด๋ผ์ด์ธํธ๋ ์ข ๋ฃํ ์์ผ ์ฐ๊ฒฐ์ ๊ณ์ ์ข ๋ฃํ์ง ๋ชปํ๋ ๊ฑธ๊น?
๋คํํ ๊ทธ๊ฑด ์๋๋ค. Active Close ์์๋ Fin ์ ํธ๋ฅผ ๋ณด๋ด๊ณ ์ผ์ ์๊ฐ ๋์ ์๋ต์ ๋ฐ์ง ๋ชปํ๋ฉด ์ฐ๊ฒฐ์ ์ข ๋ฃํ๋ค. ๋ฌธ์ ๋ ์๋ฒ์์ ์์ผ ์ฐ๊ฒฐ์ ๋์์์๋ ๋ถ๊ตฌํ๊ณ ํด๋ผ์ด์ธํธ๋จ์ CloseWait์ ๋จ๋๊ฒ์ด๋ค.
(๋ฌผ๋ก ๋ฐ๋์ ์ํฉ๋ ๋ฌธ์ ๊ฐ ๋๋ค)
์ง๊ธ ๋น์ฅ TCP ํธ๋์์ดํฌ๋ฅผ ๊ตฌํํ ์์ ๋ ์๊ฑฐ๋์โฆ ์๊ฐ๋ ์์๊ธฐ ๋๋ฌธ์ ๊ธํ๋๋ก URLSession์ ์ด์ฉํ์ ๋์ ๊ฒฐ๊ณผ๋ฅผ ์ดํด๋ณด์๋ค.
โโโโ
์ ์์ ์ผ๋ก ์์ผ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ ๊ฒ์ ํ์ธ ํ ์ ์์๋ค.
์ฌ์ง์ด Starscream 3.1.1 ๋ฒ์ ์ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ ์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ ํ์ธํ๋ค.
3.x โ 4.x Starscream ์ผ๋ก ๊ฐ๋ฉด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ง์ด ๋ฐ๋์ด์ ์ ํํ ์์ธ์ ํ์ ํ์ง๋ ๋ชปํ์ง๋ง ์๋์ ๊ฐ์ ๋ฌธ์ ๋ก ์์ํ๋ค.
public convenience init(request: URLRequest, certPinner: CertificatePinning? = FoundationSecurity(), compressionHandler: CompressionHandler? = nil, useCustomEngine: Bool = true) {
if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *), !useCustomEngine {
self.init(request: request, engine: NativeEngine())
} else if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
self.init(request: request, engine: WSEngine(transport: TCPTransport(), certPinner: certPinner, compressionHandler: compressionHandler))
} else {
self.init(request: request, engine: WSEngine(transport: FoundationTransport(), certPinner: certPinner, compressionHandler: compressionHandler))
}
}
ํ์ฌ 4.x starscream์ ๊ฒฝ์ฐ, socket์ initํ๋ ์์ ์ userCustomEngine์ ๋ํดํธ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํ๋๋ก ์ค์ ๋์ด์ TCPTransport๋ฅผ ์ฌ์ฉํ๋ WSEngine์ ์ฌ์ฉํ๊ฒ ๋๋ค.
socket = WebSocket(request: request, engine: NativeEngine())
์ผ๋ก ํ
์คํธ ํด๋ณธ ๊ฒฐ๊ณผ ์ ์์ ์ผ๋ก ์์ผ ์ฐ๊ฒฐ์ด ์ข
๋ฃ๋๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public class NativeEngine: NSObject, Engine, URLSessionDataDelegate, URLSessionWebSocketDelegate {
private var task: URLSessionWebSocketTask?
weak var delegate: EngineDelegate?
public func register(delegate: EngineDelegate) {
self.delegate = delegate
}
public func start(request: URLRequest) {
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
task = session.webSocketTask(with: request)
doRead()
task?.resume()
}
public func stop(closeCode: UInt16) {
let closeCode = URLSessionWebSocketTask.CloseCode(rawValue: Int(closeCode)) ?? .normalClosure
task?.cancel(with: closeCode, reason: nil)
}
public func forceStop() {
stop(closeCode: UInt16(URLSessionWebSocketTask.CloseCode.abnormalClosure.rawValue))
}
public func write(string: String, completion: (() -> ())?) {
task?.send(.string(string), completionHandler: { (error) in
completion?()
})
}
public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) {
switch opcode {
case .binaryFrame:
task?.send(.data(data), completionHandler: { (error) in
completion?()
})
case .textFrame:
let text = String(data: data, encoding: .utf8)!
write(string: text, completion: completion)
case .ping:
task?.sendPing(pongReceiveHandler: { (error) in
completion?()
})
default:
break //unsupported
}
}
private func doRead() {
task?.receive { [weak self] (result) in
switch result {
case .success(let message):
switch message {
case .string(let string):
self?.broadcast(event: .text(string))
case .data(let data):
self?.broadcast(event: .binary(data))
@unknown default:
break
}
break
case .failure(let error):
self?.broadcast(event: .error(error))
}
self?.doRead()
}
}
private func broadcast(event: WebSocketEvent) {
delegate?.didReceive(event: event)
}
public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) {
let p = `protocol` ?? ""
broadcast(event: .connected([HTTPWSHeader.protocolName: p]))
}
public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
var r = ""
if let d = reason {
r = String(data: d, encoding: .utf8) ?? ""
}
broadcast(event: .disconnected(r, UInt16(closeCode.rawValue)))
}
}
NativeEngine ๊ตฌํ์ฒด๋ฅผ ํ์ธํด๋ณด๋ฉด URLSession์ ์ฌ์ฉํ๊ณ ์๋ค. (ํ
์คํธ๋ฅผ ์ํด ๋ง๋ ์ฝ๋์ ์ฑํฌ๋ก์จ 99%)
URLSessionSocket์ iOS13 ๋ถํฐ ์ง์ํ๋ฉด์, StarScream๋ URLSession์ ์ฌ์ฉํ๋๋ก ๋ณ๊ฒฝ๋๊ฒ ๊ฐ๋ค.
Engine์ด ์ ํํ๊ฒ ์ด๋ค ์ญํ ์ ๋ด๋นํด์ฃผ๊ณ ์๋์ง ๊น์ง๋ ํ์ ํ์ง ๋ชปํ์ง๋ง TCP ์ข ๋ฃ์์ ์ ํธ๋์์ดํฌ ๊ณผ์ ์ ์ํฅ์ ๋ฏธ์น๊ณ ์๊ณ , Starscream 4.x ๋ฒ์ ์ ์ฌ์ฉํ๋ค๋ฉด NativeEngine์ ์ฌ์ฉํด์ผ ํ๋ค.
Hash์ ๋ํด ๋จผ์ ๊ฐ๋ตํ๊ฒ ์ดํด๋ณด๋ฉด ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค (๋ฐํน๋
์๊ณ ๋ฆฌ์ฆ ๊ฐ์ ๋ด์ฉ)
๊ฒฐ๊ตญ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ์ด๋ค key ๊ฐ์ Hash function์ ํตํด์ ๊ณ ์ ๋ ๊ธธ์ ๋ฐ์ดํฐ๋ก mapping ํด์ Hash value๋ฅผ ์ป์ด๋ด๋ ๊ฒ์ ์๋ฏธํ๋ค.
์ ๊ตณ์ด Hash Value๋ฅผ ์ป๋ ๊ณผ์ ์ด ํ์ํ ๊น? ๋ผ๋ ์๋ฌธ์ด ๋ ๋ค๋ฉด ์์ ์์๋ฅผ ์๊ฐํด๋ณด๋ฉด ๋๋ค.
๋ง์ฝ 16์๋ฆฌ์ ์นด๋๋ฒํธ์ ์ด์ฉํด์ ํด๋น ์นด๋๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋์ ์ฐพ๋ ๋ฌธ์ ๋ฅผ ์๊ฐํด๋ณด์!
๋ค์ํ ๋ฐฉ๋ฒ์ด ์๊ฒ ์ง๋ง ์๊ฐ๋ณต์ก๋๊ฐ O(1)์ด ๋๋ ๋ฐฉ๋ฒ๋ค์ ์๊ฐํด๋ณด๋ฉด ๋จ์ํ๊ฒ Counting Sort์ ์ ์ฌํ๊ฒ 0~9999 9999 9999 9999 ํฌ๊ธฐ์ String ํ์ ๋ฐฐ์ด์ ๊ฐ์ง๊ณ ์์ผ๋ฉด ์ธ๋ฑ์ค์ ์นด๋๋ฒํธ๋ฅผ ๋ฃ์ด์ค์ผ๋ก์ O(1) ์ ๋ณต์ก๋๋ก ํด๋น ์นด๋๋ฒํธ๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋์ ์ฐพ์ ์ ์๋ค.
ํ์ง๋ง ์์ ๋ฐฉ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ต์ฅํ ๋ง์ด ์ฌ์ฉํ๋ค๋ ๋จ์ ์ด ์๋ค. ์ด๋ Hash์ ๊ฐ๋ ์ ์ด์ฉํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๋ ์ ์ฝํ๋ฉด์ O(1)์ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ์ง๋๋ก ํ ์ ์๋ค.
๋ค์ํ Hash function์ด ์๊ฒ ์ง๋ง ๋จ์ํ๊ฒ ์๋ฅผ ๋ค๋ฉด ์นด๋์ ์์ 4์๋ฆฌ๋ง์ ์์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ ์ฅํด์ ๊ตฌ๋ถํ๋ค๊ณ ์๊ฐํด๋ณด์. ์ด๋ ๊ฒ ์์์ ๊ธธ์ด์ ๋ฐ์ดํฐ๋ฅผ ๊ณ ์ ๋ ๊ธธ์ด์ ๋ฐ์ดํฐ๋ก mapping ํ๋ ํจ์๊ฐ Hash function์ด๋ค.
๊ทธ๋ ๊ฒ ๋๋ฉด ์ค๋ณต๋๋ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ง ์์๊น? ํ๋ ์๋ฌธ์ด ์๊ธธ ์ ์๋ค. Hash function์ ์ด์ฉํ๊ฒ ๋๋ฉด ์์ํ๋ ๊ฒ ์ฒ๋ผ ์ค๋ณต์ด ๋ฐ์ํด์ ์ถฉ๋
์ด ์๊ธฐ๊ฒ ๋๊ณ ์ด๋ฐ ์ถฉ๋์ด ํ์ฐ์ ์ด๋ค.
์ด๋ฐ ์ถฉ๋์ ํผํ๊ธฐ ์ํด์ ๋ค์ํ ๊ธฐ๋ฒ๋ค์ด ์กด์ฌํ๋ค. (Open Addressing, Chaning)
๊ทธ๋ผ Hashable ํ๋กํ ์ฝ์ ์ฑํํ๋ค๋ ๊ฒ์ ์ด๋ค key ๊ฐ์ ํด์ฌํจ์๋ฅผ ํตํด์ hash value๋ก ๋ง๋ค์ด ๋ผ ์ ์๋ค. ์ด๋ค key ๊ฐ์ ๊ณ ์ ํ hash value (Int)๋ก ๋ง๋ค์ด ๋ผ ์ ์์์ ์๋ฏธํ๋ค.
protocol Hashable: Equatable {
var hashValue: Int { get } // Deprecated
func hash(into hasher: inout Hasher)
}
ํ๋กํ ์ฝ์ ์ดํด๋ณด๋ฉด ์์ ๊ณต๋ถํ Equatable ํ๋กํ ์ฝ์ ์ฑํํ๊ณ ์๊ณ hash ํจ์๋ฅผ ๊ตฌํํด์ผ ํ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
Swift์ ๊ธฐ๋ณธ ์๋ฃํ๋ค์ ์ด๋ฏธ Hashable ํ๋กํ ์ฝ์ ์ฑํํ๊ณ ์๋ค. (๊ทธ๋์ ์ฐ๋ฆฌ๊ฐ ๋์ ๋๋ฆฌ ํ์ ์ ์ฌ์ฉํ ๋ key ๊ฐ์ผ๋ก ์ฌ์ฉํ ์ ์์๊ณ , Set์ ์๋ฃํ์ผ๋ก ์ฌ์ฉํ ์ ์์๋ค)
๊ตฌ์กฐ์ฒด, ์ด๊ฑฐํ, ํด๋์ค์ ๊ฐ์ด ์ฐ๋ฆฌ๊ฐ ์ปค์คํ ํ๊ฒ ๋ง๋๋ ๊ฒฝ์ฐ Hashable์ ์ฑํํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด์
// ๊ตฌ์กฐ์ฒด ๋ด์ ํ๋กํผํฐ๊ฐ ๋ชจ๋ ๊ธฐ๋ณธ ์๋ฃํ์ธ ๊ฒฝ์ฐ, Hashable ์ฑํ๋ง์ผ๋ก ๊ฐ๋ฅ
struct Human: Hashable {
let name: String
let height: Double
}
let a = Human(name: "bran", height: 27)
let b = Human(name: "bran", height: 27)
if a == b {
print("์ค์ ๊ตฌ์กฐ์ฒด๋ ๊ฐํ์
์ด๋ผ Equable์ ๋ง์กฑํ๊ณ ์๋๊ฑด๊ฐ?")
}
var dic: [Human : Int]
๊ตฌ์กฐ์ฒด์ ์ ์ฅํ๋กํผํฐ๋ ๋ชจ๋ Hashable์ ์ค์ํด์ผ ํ๋ค.
๊ตฌ์กฐ์ฒด๋ Swift 4.1 ์ดํ๋ก Hashable์ ์ฑํ๋งํด์ฃผ๋ฉด ๋๋ค.
// ์ฐ๊ด๊ฐ์ด ์๋ ์ด๊ฑฐํ์ Hashable์ ์ฑํํ์ง ์์๋ ์๋์ผ๋ก ๊ตฌํ
enum Gender {
case male
case female
}
var myDic: [Gender : Int]
enum GenderwithAge: Hashable {
case male(age: Int)
case female(man: Human)
}
์ฐ๊ด๊ฐ์ด ์๋ ์ด๊ฑฐํ์ ๊ฒฝ์ฐ Hashable์ ์ด๋ฏธ ์ฑํํ๊ณ ์๊ณ , ์ฐ๊ด๊ฐ์ด ์๋ ๊ฒฝ์ฐ ์ฐ๊ด๊ฐ์ ๋ชจ๋ Hashable์ ์ค์ํด์ผ ํ๋ค.
// ํด๋์ค๊ฐ Hashableํ๊ธฐ ์ํด์๋ ํ๋กํ ์ฝ์ ์ฑํํ๊ณ
class Man {
let name: String = "Bran"
let age: Int = 27
}
// Equatable ํ๋กํ ์ฝ์ ์ฑํํ๊ณ
extension Man: Equatable {
static func == (lhs: Man, rhs: Man) -> Bool {
return lhs.name == rhs.name && lhs.age == rhs.age
}
}
// hash ํจ์๋ฅผ ๊ตฌํํด์ผ ํ๋ค
extension Man: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(age)
}
}
ํด๋์ค๋ Equatable ํ๋กํ ์ฝ์ ์ค์ํ๊ณ hash ํจ์๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค. hash ํจ์๋ Hasher์ combine์ ์ฌ์ฉํด์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋ค.
(combine์๋ ํด๋น ํ์ ์ ๋ชจ๋ ์ ์ฅ ํ๋กํผํฐ๋ฅผ ์ ๋ฌ, ํด๋น ํ๋กํผํฐ๋ Hashable์ ์ค์ํ๊ณ ์์ด์ผ ํจ)
์ฝ๋๋ฅผ ๋ณด๋ค๋ณด๋ฉด Equatable ํ๋กํ ์ฝ์ ์ฑํํ๋ ๊ฒ์ ๋ง์ด ํ์ธํ ์ ์๋๋ฐ ์ฌ์ค ํด๋น ํ๋กํ ์ฝ์ ์ฑํํ๋ฉด
== , !=
๊ณผ ๊ฐ์ ๋๋ฑ์ฑ์ ๋น๊ตํ ์ ์๋ค ์ ๋๋ก๋ง ์๊ณ ์๋๋ฐ ์กฐ๊ธ๋ง ๋ ์์ธํ๊ฒ ์์๋ณด๊ณ ์ ํ๋ค.
let a: Int = 1
let b: Int = 2
if a == b {
print("True")
}
์์ ๊ฐ์ด a == b
๊ฐ ๊ฐ๋ฅํ๋๊ฑด Int(Struct)๊ฐ Equatable ํ๋กํ ์ฝ์ ์ค์ํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
์ฐ๋ฆฌ๊ฐ ๋ง๋ ํด๋์ค๋ ๊ตฌ์กฐ์ฒด์ ์ธ์คํด์ค๊ฐ ๋์ผํ์ง ํ์ธํ๊ธฐ ์ํด์๋ Eqautable ํ๋กํ ์ฝ์ ์ฑํ์์ผ์ผ ํ๋ค.
import Foundation
class A: Equatable {
var num: Int
init(num: Int) {
self.num = num
}
static func == (lhs: A, rhs: A) -> Bool {
return lhs.num == rhs.num
}
}
let a = A(num: 10)
let b = A(num: 10)
if a == b {
print("equal") // equal
}
Equatable ํ๋กํ ์ฝ์ ๋ค์ฌ๋ค๋ณด๋ฉด statuc func == (lhs: Self, rhs: Self) โ Bool
๋ฉ์๋๋ฅผ ๋ฐ๋์ ๊ตฌํํด์ผ ํจ์ ์ ์ ์๋ค.
ํด๋์คA์ Equatable ํ๋กํ ์ฝ์ ์ฑํํ๊ณ ํด๋์ค A์ ํ๋กํผํฐ์ธ num์ด ๋์ผํ์ง๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋ณํด์ Bool ๊ฐ์ ๋ฆฌํดํ๋๋ก ํจ์๋ฅผ ๊ตฌ์ฑํ๊ณ ํ ์คํธ ํด๋ณด์
์ด๋ lhs.num == rhs.num์ด ๊ฐ๋ฅํ ์ด์ ๋ Int ํ์
์ด Equatable ํ๋กํ ์ฝ์ ์ด๋ฏธ ์ฑํํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค!
๊ทธ๋ผ A๋ฅผ ํตํด ์์ฑ๋ ์ธ์คํด์ค๋ฅผ ์ง์ ๋๋ฑ๋น๊ต๋ฅผ ํ ์ ์๋๊ฒ์ ํ์ธํ ์ ์๋ค.
์ ๋ฆฌํ๋ค๋ณด๋ ์ฐธ์กฐํ์ ๊ณผ ๊ฐํ์ ์ ๋ํ ์ ํํ ์ ๋ฆฌ๊ฐ ํ๋ฒ ํ์ํ ๊ฒ ๊ฐ๋ค!
์๋ง Swift๋ก ์ฝ๋๋ฅผ ๋ง์ด ์ ํด๋ณด์ ๋ถ๋ค์ ์์ฃผ ์ฌ์ฉํ๋ ๋ฉ์๋์ ์ํ์ ๊ด์ฐฐํด๋ณด๋ฉด @autoclosure
๋ผ๋ attribute๋ฅผ ๋ณธ์ ์ด ์์ํ
๋ฐ์. ํน์ ์ด๋ค ์๋ฏธ๋ฅผ ๊ฐ์ง๋์ง๋ ์ ํํ ์๊ณ ๊ณ์ ๊ฐ์?? (์ ๋ ๋ชฐ๋๋ต๋๋ค~)
{ } ์์ด ํํ์๋ง ์ ์ด๋ ํด๋ก์ ๋ฅผ ๋ํํด์ฃผ๋ ๊ธฐ๋ฅ
์๋์ผ๋ก ํด๋ก์ ๋ฅผ ๋ง๋ค์ด์ฃผ๋ ๊ธฐ๋ฅ์ด ๋์ฒด ์ ํ์ํ๊ฑธ๊น์โย ์์๋ฅผ ํตํด์ ํ๋ฒ ์ดํด ํด๋ด ์๋ค.
func goodMorning(morning: Bool, whom: String) {
if morning {
print("Good Morning, \(whom)")
}
}
func giveName() -> String {
print(#function, ": Called")
return "Bran"
}
goodMorning(morning: true, whom: "์ ์") // Good Morning, ์ ์
goodMorning(morning: false, whom: giveName()) // giveName(): Called
๋งค๊ฐ๋ณ์ whom์ ํ์ ์ String ํ์ ์ด๊ธฐ ๋๋ฌธ์ String ํ์ ์ ๋ฐํํ๋ giveName()์ด๋ผ๋ ๋ฉ์๋์ ๋ฐํ๊ฐ์ ๋งค๊ฐ๋ณ์๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ์ฌ ๋ฉ์๋ ๊ตฌ์กฐ์์๋ whom ๋งค๊ฐ๋ณ์๋ฅผ ์ค์ ๋ก ์ฌ์ฉํ์ง ์๋ ์์ ์๋ ๊ฐ์ ๋ณต์ฌํด์ ๊ฐ์ ธ์ค๋ ๋ฌธ์ ์ ์ด ์์ต๋๋ค. whom์ด๋ผ๋ ๋งค๊ฐ๋ณ์๋ morning์ด true์ธ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ๋ฉด ๋๋๋ฐ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ ์์ ์ ์ธ์giveName()์ด๋ผ๋ ๋ฉ์๋๊ฐ ์คํ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ฃ .
์์ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น์? ์ฌ์ค ๋ฐฉ๋ฒ์ด์ผ ๋ค์ํ๊ฒ ์๊ฒ ์ง๋ง ํธ์ถ๋๊ธฐ ์ ๊น์ง๋ ๋ด๋ถ ์ฝ๋๋ฅผ ์คํ์ํค์ง ์๋ ํด๋ก์ ์ ํน์ฑ์ ์ด์ฉํ๋ฉด ์ฝ๊ฒ ํด๊ฒฐํ ์ ์์ต๋๋ค. Lazy evaluation
func goodMorning(morning: Bool, whom: () -> String) {
if morning {
print("Good Morning, \(whom())")
}
}
func giveName() -> String {
print(#function, ": Called")
return "Bran"
}
goodMorning(morning: true, whom: "์ ์") // Error
goodMorning(morning: true, whom: {"์ ์"}) // Good Morning, ์ ์
goodMorning(morning: false, whom: giveName) //
morning์ด false์ธ ๊ฒฝ์ฐ์๋ () โ String ํ์ ์ ๋ฉ์๋๊ฐ ์คํ๋์ง ์์์์ ํ์ธํ ์ ์์ต๋๋ค.
(=ํด๋ก์ ํํ์ ํ๋ผ๋ฏธํฐ whom์ ์ธ์๊ฐ ํ๋ผ๋ฏธํฐ์ ์ ๋ฌ๋๋ ์์ ์ด ์๋ ํจ์๋ด๋ถ์์ ํธ์ถ๋๋ ์์ ์ ์คํ๋๊ฒ ๋์์ต๋๋ค.)
ํ์๋ง, whom ํ๋ผ๋ฏธํฐ์ ํ์
์ด ๋ช
์์ ์ธ ํด๋ก์ ๋ก ๋ณ๊ฒฝ๋๋ฉด์ ์ด์ ์ ๋ฉ์๋์ฒ๋ผ String ํ์
์ ์์๋ ๋ณ์๋ฅผ ์ธ๋ก์ ๋๊ฒจ์ค ์ ์๊ฒ ๋์๋๋ฐ ์ด๋ autoclosure
๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
func goodMorning3(morning: Bool, whom: @autoclosure () -> String) {
if morning {
print("Good Morning, \(whom())")
}
}
goodMorning3(morning: false, whom: "์ ์") //
goodMorning3(morning: true, whom: "๋ค์นด") // Good Morning, ์ ์
goodMorning3(morning: true, whom: giveName()) // giveName() : Called
// Good Morning, Bran
์ฆ autoclosure๋ฅผ ์ด์ฉํ๋ฉด ์ฐ๋ฆฌ๊ฐ ๋ฐ๋ก { } ์์ด ํํ์๋ง ์ ์ด๋ ์๋์ผ๋ก ํด๋ก์ ๋ก ๋ํํด์ค๋๋ค.
ํจ์์ ํ๋ผ๋ฏธํฐ๊ฐ autoclosure attribute๋ฅผ ์ด์ฉํ๊ณ ์๋ ๊ฒฝ์ฐ, ํจ์์ ์ํ์ ์ ๋ค์ฌ๋ค ๋ณด์ง ์์ผ๋ฉด ํจ์์ ํ๋ผ๋ฏธํฐ ํ์ ์ด ํด๋ก์ ์ ๋ฐํํ์ ์ผ๋ก ์ฐฉ๊ฐํ ์ ์์ต๋๋ค.
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ๋ ์ธ์๋ ๋ฐํํ์ ์ ์์๋ ๋ณ์๋ฅผ ์ ์ด๋ ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์ผ๋๊น์..
ํด๋น ์ฌ์ค์ ๋ชจ๋ฅด๊ณ ํ๋ผ๋ฏธํฐ์ ํ์
์ ์๋ชป ์ธ์งํ๋ ๊ฒ๋ ์ถฉ๋ถํ ๋ฌธ์ ๊ฐ ๋ ์ ์์ง๋ง ๋ ํฐ ๋ฌธ์ ๋ ์ค์ ํ์
์ด ํด๋ก์ ๋ผ๋๋ฐ ์์ต๋๋ค. ์์์ ํด๋ก์ ๋ ์ฌ์ฉํ๋ฉด ํธ์ถ๋๊ธฐ ์ ๊น์ง๋ ์คํ๋์ง ์๋ **Lazy evaluation
ํน์ง์ด ์๋ค๊ณ ์ ๋ฆฌํ์ต๋๋ค.
๊ทธ๋ผ ์ฝ๋์ ๋ฐ๋ผ์ ํด๋ก์ ์ **Lazy evaluation
ํน์ง ๋๋ฌธ์ ์์์น ๋ชปํ ๋์์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
() โ T ํํ์ ํด๋ก์ ๋ง ์ฌ์ฉ๊ฐ๋ฅํฉ๋๋ค.
[@autoclosure what, why and when](https://medium.com/ios-os-x-development/https-medium-com-pavelgnatyuk-autoclosure-what-why-and-when-swift-641dba585ece)
์ด๋ฒ ์ ๋ฆฌ์์๋ Cocoapod์ ์ด์ฉํด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์์ ๋ ์ป์ ์ ์๋ ์ฅ,๋จ์ ์ ๋ํด์๋ ์ ๋ฆฌํ์ง ์๊ณ Cocoapod์ ์ด๋ฐ์์ผ๋ก ์ด์ฉํ ์ ์๋ค ๋๋์ผ๋ก ์ ๋ฆฌํด๋ณผ ์์ ์ด๋ค.
์์
ํ๊ณ ์๋ ํ๋ก์ ํธ๊ฐ ์ปค์ง๋ฉด์ ์์ฐ์ค๋ฝ๊ฒ ๋์์ธ ๋ฆฌ์์ค ํ์ผ๋ ๋ง์์ง๊ณ ๊ทธ์ ๋์ ๋๋ ์ฝ๋๋ค๋ ๋ง์์ง๋ ๋ฌธ์ ๋ฅผ ๊ฒช๊ณ ์์๋ค. ๋ฌผ๋ก r.swift
๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ ๋ฆฌ์ค์ ๊ด๋ จ ์ฝ๋๋ฅผ ์๋์ผ๋ก ์์ฑํ ์ ์์ง๋ง ์๋๋ก์ด๋์ ํ์ผ๋ช
์ ๋ง์ถ๋๋ฐ ์ด๋ ค์์ด ์์๋ค.
๋ํ ๋์์ธ ์์คํ ํ์ผ์ด ๋ง๋ค์ด์ง๋ฉด์ ๋์์ธ์์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ฒํผ, ํ์ , ๋ฐํ ์ํธ ๋ฑ๋ฑ์ด ์๊ธฐ๋ฉด์ ์ด๋ฐ ํ์ผ๋ค์ ํ๋ฐ ๋ชจ์ ๋ชจ๋ํ ์ํฌ ํ์์ฑ์ ๋๊ปด์ ๋ฐฉ๋ฒ์ ์ฐพ๋์ค ์์ ๋ฐฉ๋ฒ์ ํตํด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ณผ์ ๊ณผ ๋ ์๋กญ๊ฒ ๋ฐ์ํ ๋ฌธ์ ์ ๋ค์ ์์นด์ด๋น ํด๋ณด๊ณ ์ ํ๋ค.
ํฐ๋ฏธ๋์์ ์์ ๊ฐ์ด pod lib create {๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ช
}
์ ์
๋ ฅํ๋ฉด ํ๋ก์ ํธ ํ์ผ์ด ํ๋ ์์ฑ๋๋ค.
Pod::Spec.new do |s|
s.name = 'BranUI'
s.version = '0.1.0'
s.summary = 'A short description of BranUI.'
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
# ๋ฏธ์ค์ ->
s.homepage = 'https://github.com/Brandnew-one/BranUI'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Brandnew-one' => 'cold929@naver.com' }
s.source = { :git => 'https://github.com/Brandnew-one/BranUI.git', :tag => s.version.to_s }
# <- ๋ฏธ์ค์
s.ios.deployment_target = '15.0'
s.source_files = 'BranUI/Classes/**/*'
s.resources = "BranUI/Assets/*.xcassets"
s.resource_bundles = {
'BranUI' => ['BranUI/Assets/*']
}
s.dependency 'lottie-ios'
end
ํ์ฌ cocoapods์ ํตํด์ ๋ง๋ค๊ณ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ spec์ ๋ํ ์ ๋ณด๊ฐ ์ ์ฅ๋๋ ํ์ผ์ด๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ผ์ ๋ง๋ค์ด์ ๋ฐฐํฌํ๋๊ฒ ์๋ local๋ก ์ฌ์ฉํ ์์ ์ด๊ธฐ ๋๋ฌธ์ ๋ฏธ์ค์ ์ผ๋ก ํ๊ธฐ๋ ๋ถ๋ถ์ ์ ์ธํ๊ณ ์๋ ๋ถ๋ถ์ ์ง์คํด๋ณด์.
ํ์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ๋์์ธ ๋ฆฌ์์ค ํ์ผ์ ํฌํจํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฆฌ์์ค ํ์ผ ๋๋ ํ ๋ฆฌ๋ฅผ ์ค์ ํด์ผ ํ๊ณ motion๊ณผ ๊ฐ์ ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ฅผ ์ํด์ Lottie
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํด์ค ์์ ์ด๋ค.
Lottie๋ฅผ ์ถ๊ฐํ๊ธฐ ๋๋ฌธ์ BranUI์ Podfile์์ Lottie๋ฅผ ์ค์นํ๋ ๊ณผ์ ์ด ํ์ํ๋ค!
use_frameworks!
platform :ios, '10.0'
target 'BranUI_Example' do
pod 'BranUI', :path => '../'
target 'BranUI_Tests' do
inherit! :search_paths
pod 'lottie-ios'
end
end
โ๏ธpodspec ํ์ผ์ ์ค์ ํ ๋, resouce์ source_file๋ค์ ๋๋ ํ ๋ฆฌ๋ฅผ ์ค์ ํ๋ ๋ถ๋ถ์ด ์์๋๋ฐ ํด๋น ๋๋ ํ ๋ฆฌ์ ๋ฆฌ์์ค ํ์ผ๊ณผ ์ฝ๋๋ฅผ ์์ฑํ๊ณ .pbx ํ์ผ์ด ํด๋น ํ์ผ์ ์ฝ์ด์ฌ ์ ์๋๋ก ์ค์ ํด์ค์ผ ํ๋ค
Development Pods ๋๋ ํ ๋ฆฌ์ ์ ์ฅ๋ ํ์ผ๋ค์ ์์ ๊ฐ์ด podspec์ ์ ์๋ ์์น์ ์๋ ํ์ผ์ด์ด์ผ ํ๋ค.
import Foundation
private class BranBundleClass {}
extension Bundle {
class var branUI: Bundle {
Bundle(for: BranBundleClass.self)
}
}
ํ์ฌ ํ๋ก์ ํธ์ Bundle ํ์ผ์ ์ ๊ทผํ ์ ์๋๋ก ์ค์ ํด์ค๋ค.
import SwiftUI
import UIKit
extension Image {
fileprivate static let bundle: Bundle = .branUI
}
extension UIImage {
fileprivate static let bundle: Bundle = .branUI
fileprivate convenience init(
_ name: String,
bundle: Bundle
) {
self.init(named: name, in: bundle, compatibleWith: nil)!
}
}
extension Image {
public static let foodCart: Image = Image(
"icons8-food-cart",
bundle: bundle
)
}
extension UIImage {
public static let foodCart: UIImage = UIImage(
"icons8-food-cart",
bundle: bundle
)
}
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'BranTestProject' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Local Pods for BranTestProject
pod 'BranUI', :path => "../BranUI/"
# Pods for BranTestProject
end
์ค์ ํ๋ก์ ํธ์์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์ ๋๊ฒฝ๋ก๋ฅผ ํตํด์ ๊ฐ์ ธ์ค๋ฉด ๋๋ค.
.
โโโ git
โ โโโ BranTestProject
โ โ โโโ ...
โ โ .
โ โ .
โ โ .
โ โ
โ โโโ BranUI
โ โ โโโ ...
์ค์ ํ๋ก์ ํธ๋ https://github.com/Brandnew-one/LocalPod ์์ ํ์ธํ ์ ์์ต๋๋ค.
์ ๋ฆฌํ๊ธฐ
let testLabel: UILabel = {
let label = UILabel()
label.text = "Lunch Screen"
label.font = .systemFont(ofSize: 30, weight: .bold)
label.textColor = .black
return label
}()
UIKit์์ ๋ทฐ๋ฅผ ์ฝ๋๋ก ์ง๋ณด์ ๋ถ๋ค์ ์๋ง ์์ ๊ฐ์ ํ์์ผ๋ก ๋ทฐ ์ปดํฌ๋ํธ๋ค์ ๋ง๋ ์ฝ๋๋ฅผ ๋ง์ด ๋ณด์ จ์๊ฒ๋๋ค. (์ ๋ ์ฌ์ค ์ค์ ๋ง ํด์ ์ ๋ชจ๋ฆ ๋๋ค ํคํฟ)
์ ๋ํด์ ์ ๋ฆฌํด๋ณผ ์์ ์ ๋๋ค.
์์ ์ฝ๋๋ฅผ ํตํด์ ์ด๋ป๊ฒ UILabel ์ธ์คํด์ค๊ฐ ์์ฑ๋๋์ง ๋ฐ๋ก ์ดํดํ์
จ๋์? ์ง๋ฌธ์ ๋ฐ๊ฟ์ ()
๊ฐ ์์ด์ผ ํ๋ ์ด์ ๋ฅผ ์ ํํ ํ์
ํ์
จ๋์?
์๋ง ํด๋ก์ ์ ๋ํด ์ ๋๋ก ํ์ต ํ์ จ๋ ๋ถ๋ค์ ์ ์ง๋ฌธ์ ๋๋ตํ์๋๋ฐ ํฐ ์ด๋ ค์์ด ์์์ ๊ฒ ๊ฐ์ต๋๋ค.
ํ๋์ฉ ๋ฏ์ด ๋ณด๊ฒ ์ต๋๋ค.
// 1)
{
let label = UILabel()
label.text = "Lunch Screen"
label.font = .systemFont(ofSize: 30, weight: .bold)
label.textColor = .black
return label
}
์ฐ์ ์์ ์ { } ๋ก ๊ฐ์ธ์ง 1๋ฒ ์ฝ๋๋ญ์น(?)์ ํ์ ์ด ๋ฌด์ ์ผ๊น์?
// 2)
let b = { () -> UILabel in
let label = UILabel()
label.text = "Lunch Screen"
label.font = .systemFont(ofSize: 30, weight: .bold)
label.textColor = .black
return label
}
์๋ง 2๋ฒ ์ฝ๋๋ฅผ ๋ณด๋ ์๊ฐ 1๋ฒ ์ฝ๋ ๋ญ์น์ ํ์ ์ด () โ UILabel ํ์ ์ ํด๋ก์ ์์ ๋์น ์ฑ์ จ์๊ฒ๋๋ค.
ํ๋ฆฌ์ จ๋ค๊ณ ํด๋ ์ ํ ๊ฑฑ์ ํ์ค ํ์๊ฐ ์์ต๋๋ค. ์ฌ์ค 1๋ฒ ์ฝ๋๋ Swift๋ ํ์ ์ถ๋ก ์ ๋ชปํ๋ ์ฝ๋๋๊น์ ใ ใ
์ 1๋ฒ ์ฝ๋๋ฅผ Swift๊ฐ ํ์ ์ถ๋ก ์ ์คํจํ๋์ง๋ ๋ง์ง๋ง์ ๋ฐ๋ก ์ ๋ฆฌํ๊ธฐ๋ก ํ๊ณ ํ๋ ์ ๋ฆฌ๋ฅผ ์ด์ด๊ฐ๋ด ์๋ค.
let testLabel: UILabel = {
let label = UILabel()
label.text = "Lunch Screen"
label.font = .systemFont(ofSize: 30, weight: .bold)
label.textColor = .black
return label
}()
์ด์ ()
๊ฐ ๋ถ์ด์ผ๋ง ํ๋ ์ด์ ๋ฅผ ์์๊ฒ ๋์? () โ UILabel ํ์
์ ํด๋ก์ ๋ฅผ ์คํ์ํจ ๊ฒฐ๊ณผ๊ฐ์ testLabel์ ๋ฃ์ด์ฃผ๊ณ ์๋ ๊ฑฐ์ฃ !!
๊ทธ๋ผ ํด๋ก์ ๋ฅผ ํตํด์ ์ฐ์ฐ๋ ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํด์ ํ๋กํผํฐ๋ฅผ ์ด๊ธฐํ ์ํค๊ณ ์๋๋ฐ ์ฐ์ฐ ํ๋กํผํฐ์์ ์ฐจ์ด์ ์ ๋ฌด์์ผ๊น์?
์ฐจ์ด์ ์ ์ฐ์ฐ์ ํ์์ ์์ต๋๋ค. ์ฐ์ฐ ํ๋กํผํฐ๋ ํธ์ถ๋ ๋๋ง๋ค ์ฐ์ฐ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๋ฐ๋ฉด ํด๋ก์ ๋ฅผ ํตํด ์ด๊ธฐํ๋ ์ ์ฅ ํ๋กํผํฐ๋ ์ต์ด ์ด๊ธฐํ ์์ ์ ํ๋ฒ ์ฐ์ฐ๋๊ณ ์ดํ์๋ ์ ์ฅ๋ ๊ฐ์ ์ฌ์ฉํ๋ ์ฐจ์ด๊ฐ ์์ต๋๋ค.
let a = {
let label = UILabel()
label.text = "Lunch Screen"
label.font = .systemFont(ofSize: 30, weight: .bold)
label.textColor = .black
return label
}
Swift๋ a ์ ํ์ ์ ์ถ๋ก ํ์ง ๋ชปํฉ๋๋ค. (์ปดํ์ผ ํ์๋ฉด ํ์ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค)
ํด๋ก์ ์ ์ธ์๊ฐ ์๊ณ ๋ฐํํ์ ์ ์ ์ ์๋๋ฐ ์ () โ UILabel์ด๋ผ๋ ํ์ ์ ์ถ๋ก ํ์ง ๋ชปํ ๊น์?
UILabel์ ์ธ์คํด์ค๋ฅผ ํด๋ก์ ๋ด๋ถ์์ returnํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ํด๋ก์ ๋ด๋ถ์ ๋ ๋ฆฝ์ ์ธ ์ง์ญ๋ณ์์ label์ ์ธ๋ถ์์ ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ ํด๋ก์ ์ธ๋ถ์ ์ ์ฅ์์๋ ํ์ ์ถ๋ก ์ ํ ์ ์์ต๋๋ค!!
๋์ผํ ์ด์ ๋ก
let testLabel: UILabel = {
let label = UILabel()
label.text = "Lunch Screen"
label.font = .systemFont(ofSize: 30, weight: .bold)
label.textColor = .black
return label
}()
์์๋ UILabel์ด๋ผ๋ ํ์ ์ ๋ช ์ํด์ค์ผ ํฉ๋๋ค!!
[[์ค์ํํธ(Swift) ํ๋ก๊ทธ๋๋ฐ] - Closure๋ฅผ ์ด์ฉํด ์ ์ฅ ํ๋กํผํฐ ์ด๊ธฐํํ ๋ ๋ค์๋ ์๋ฌธ์ ๋ค](https://jayb-log.tistory.com/259)
[Why can't the Swift compiler infer this closure's type?](https://stackoverflow.com/questions/42534207/why-cant-the-swift-compiler-infer-this-closures-type)
SwiftUI๋ฅผ ์ด์ฉํด์ TabBar๋ฅผ Hidden ์ํค๋ ๊ฒฝ์ฐ ๋ฐ์ํ๋ ๋ฒ๊ทธ๋ฅผ ๊ณต์ ํด๋ณด๊ณ ์ ํ๋ค.
SwiftUI 3.0 ๊ธฐ์ค์ผ๋ก TabBar Hidden์ SwiftUI ์์ฒด์ ์ผ๋ก ์ง์ํด์ฃผ์ง ์๋๋ค
(SwiftUI 4.0์ ์ง์ํด์ค๋ค. ์ ํ๋๋ค์..)
import SwiftUI
import UIKit
struct TabBarAccessor: UIViewControllerRepresentable {
var callback: (UITabBar) -> Void
private let proxyController = ProxyViewController()
func makeUIViewController(context: UIViewControllerRepresentableContext<TabBarAccessor>) -> UIViewController {
proxyController.callback = callback
return proxyController
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<TabBarAccessor>) {
}
// ํญ๋ฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํ ํฌํผ ๋ทฐ์ปจ
private class ProxyViewController: UIViewController {
var callback: (UITabBar) -> Void = { _ in }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let tabBarController = self.tabBarController {
callback(tabBarController.tabBar)
}
}
}
}
extension View {
func setTabBarVisibility(isHidden: Bool) -> some View {
background(TabBarAccessor(callback: { tabBar in
tabBar.isHidden = isHidden
tabBar.layoutIfNeeded()
}))
}
}
SwiftUI์์ ์์ฒด์ ์ผ๋ก ์ง์ํด์ฃผ์ง ์๊ธฐ ๋๋ฌธ์ UIViewController๋ฅผ ๋ํ์์ผ์ UIKit ๊ธฐ๋ฐ์ผ๋ก ViewWillAppear ์์ ์ ํด๋น ๋ทฐ์ปจ์์ tabBar๋ฅผ ์ฐพ์์ ํด๋ก์ ํํ๋ก ๋๊ฒจ์ฃผ๋๋ก ๊ตฌ์ฑํ๊ณ ViewModifier๋ฅผ ํตํด ํด๋น tabBar๋ฅผ hidden ์ํฌ ์ ์๋๋ก ํ๋ ์ฝ๋๋ฅผ ์ฐพ์์ ์ฌ์ฉํ๊ณ ์๋ค.
๊ทธ๋์ TabBar๊ฐ ์กด์ฌํ๋ RootView์์ push ๋๋ ๊ฒฝ์ฐ, ์๋กญ๊ฒ ๋ํ๋ ํ๋ฉด์์ TabBar๋ฅผ hidden ํด์ ์ฌ์ฉํ๊ณ ์๋ค.
์ ์์ ์ผ๋ก ์ ๋์ํ๋ ๊ฒ ์ฒ๋ผ ๋ณด์์ง๋ง ์์ ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์ผ ๋ง์๋คโฆ
push ๋ ํ๋ฉด์ ์ตํ๋จ์ View๋ฅผ ๊ทธ๋ฆฌ๋ฉด TabBar๋ ๋ณด์ด์ง ์์ง๋ง ๊ธฐ์กด์ TabBar๋งํผ์ ๋์ด๋ฅผ ๋จน๊ณ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ์ง์ด ์ด ์ํ์์ background, inActive โ active ์ํ๋ก ์ ํ์ด ๋ฐ์ํ๋ฉด ํด๋น ๋์ด๊ฐ ์ฌ๋ผ์ง๋ค.
background, inActive โ active ์ํ๋ก ๋ณ๊ฒฝ๋ ๋, SwiftUI View์์ ์ ํํ๊ฒ ์ด๋ค ์ผ์ด ๋ฐ์ํ๋์ง ์์๋ณด์ง๋ ์์์ง๋ง ๊ฐ๋จํ๊ฒ ์ผ๋งค(?)๋ก ํด๊ฒฐ๋ฒ์ ์ฐพ์ ์ ๋ฆฌํด๋๊ณ ์ ํ๋ค.
์์์ ํตํด์ ์ด๋ค ๋ฐฉ๋ฒ์ ์ด์ฉํ๋์ง ์ง์ํ ์ ์์๊ฒ ๊ฐ์๋ฐ ํด๊ฒฐ๋ฒ์ bottom์ safeArea๋ฅผ ๋ฌด์ํ๋ ๊ฒ์ด๋ค!
(์ฌ์ง์ด ์ ์์์๋ ๋์์์ง ์์ง๋ง Bottom SafeArea๊ฐ ์กด์ฌํ์ง ์๋ ๋ ธ์น๊ฐ ์๋ ๋ชจ๋ธ์์๋ ๋์ผํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋๋ฐ ๋์ผํ๊ฒ ํด๊ฒฐ๊ฐ๋ฅํ๋ค)
์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ ViewModifier๋ ์์์ ์ํฅ์ ๋ฐ๊ธฐ ๋๋ฌธ์
safeArea Bottom height ๋งํผ padding์ ์ฃผ๊ณ ๋ ์ดํ์ Tabbar๋ฅผ hidden ์ํค๋ฉด์ safeArea๋ฅผ ๋ฌด์ํด์ผ ํ๋ค!
import SwiftUI
extension UIApplication {
var keyWindow: UIWindow? {
connectedScenes
.compactMap {
$0 as? UIWindowScene
}
.flatMap {
$0.windows
}
.first {
$0.isKeyWindow
}
}
}
private extension UIEdgeInsets {
var swiftUiInsets: EdgeInsets {
EdgeInsets(top: top, leading: left, bottom: bottom, trailing: right)
}
}
private struct SafeAreaBottomKey: EnvironmentKey {
static var defaultValue: CGFloat {
UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets.bottom ?? 0.0
}
}
extension EnvironmentValues {
var safeAreaBottom: CGFloat {
self[SafeAreaBottomKey.self]
}
}
struct FriendDetailView: View {
@Environment(\.safeAreaBottom)
var safeAreaBottom
var body: some View {
VStack {
Text("์๋ฌ ์ผ์ด์ค")
.font(.title2)
Spacer()
Button(
action: { },
label: {
Text("๋ฒํผ์ ์์น๊ฐ?")
.font(.body)
}
)
}
.padding(.bottom, safeAreaBottom)
.setTabBarVisibility(isHidden: true)
}
}
์ ์๋์ ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ ์ฒด์ฝ๋๋ ์ฌ๊ธฐ์์ ํ์ธ ํ ์ ์์ต๋๋ค.
๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ฑ
๋ณด๋ค๋ ์ผ๋งค๋ก ์ด๋ป๊ฒ๋ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์ฐพ์๋๋ฐ ํด๊ฒฐ๋ฐฉ๋ฒ์ ์์๋ ๋ถ์ [email protected]์ผ๋ก ์ฐ๋ฝ ์ฃผ์๋ฉด
์ ๋ง ๋๋ฌด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค
๊ฐ๋ตํ๊ณ ์ดํดํ ๋ด์ฉ ์์ฃผ๋ก ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๊ณ ์ฃผ๋ง์ ์์ ์ฝ๋์ ํจ๊ป ๋ ์์ธํ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค!
์ฐธ๊ณ ํ๋ฉด ์ข์ WWDC21 ์ฐธ๊ณ ํ๊ธฐ
์ฐธ๊ณ ํ๋ฉด ์ข์ ๋ธ๋ก๊ทธ
Swift๋ ARC๋ฅผ ํตํด์ ๊ฐ์ฒด์ ๋ํ ์ฐธ์กฐ ์นด์ดํธ๋ฅผ ๊ด๋ฆฌํ๊ณ 0์ด ๋๋ฉด ์๋์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํด์ฃผ๋ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ค.
ํ์ง๋ง ๋๊ฐ ์ด์์ ๊ฐ์ฒด๊ฐ ์๋ก์ ๋ํ ๊ฐํ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง๋ฉด ์ํ์ฐธ์กฐ๊ฐ ๋ฐ์ํ๊ฒ ๋ผ์ ๋ฉ๋ชจ๋ฆฌ ๋์ ํ์์ด ์ผ์ด๋๊ฒ ๋ฉ๋๋ค.
์ด๋ฐ ํ์์ ๋ง๊ธฐ ์ํด์ ์ฐ๋ฆฌ๋ weak
๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ํ์ถ ํด๋ก์ ์์ [weak self]๋ฅผ ์์ฃผ ์ ํ๊ฒ ๋ฉ๋๋ค. ํ์ถ ํด๋ก์ ์์ ๊ฐ์ ์บก์ฒํ๋ ๊ณผ์ ์ ๋ช ์์ ์ผ๋ก self๋ฅผ ์์ฑํด์ค์ผ ํ๋๋ฐ ์ด๋ self์ ๋ํ ์ํ์ฐธ์กฐ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ํธํ์ ๋ณตํธํ์ ๊ฐ์ ๊ฐ์ ํค๋ฅผ ์ฌ์ฉํ๋ ์๊ณ ๋ฆฌ์ฆ์ ์๋ฏธํ๋ค.
์ฆ, ํด๋ผ์ด์ธํธ์์ ๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ ํตํด์ Request Body๋ฅผ ์ํธํ ์์ผ์ ๋ณด๋ด๊ฒ ๋๋ ๊ฒฝ์ฐ ์๋ฒ์์ ํด๋น ๋ด์ฉ์ ๋ณตํธํ ํ๊ธฐ ์ํด์๋ ํด๋ผ์ด์ธํธ์์ ์ํธํํ๋๋ฐ ์ฌ์ฉํ๋ ํค๋ฅผ ์๋ฒ์๋ ๋ณด๋ด์ค์ผ ํ๋ค
์ค์ ๋ก ๋์นญํค๋ฅผ ์ํธํ๋ฅผ ์ฌ์ฉํ ๋, ๋์นญํค๋ฅผ ์์ ํ๊ฒ ์ ๋ฌํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ด๋ค.
์ค์ ๋ณด์์ ๋ํด ์กฐ์๊ฐ ๊น์ง ์์ ๋์นญํค๋ฅผ ์ด๋ป๊ฒ ์์ ํ๊ฒ ์ ๋ฌํ์ง์ ๋ํ ๋ค์ํ ๋ฐฉ๋ฒ์ ๋ํด์๋ ์์ง ๋ชปํ์ง๋ง ์ํธํ-๋ณตํธํ
ํ ๋ด์ฉ์ ๋์นญํค ์ํธํ๋ฅผ ์ด์ฉํ๊ณ ๋์นญํค ์ํธํ๋ฅผ ๋ค์ ๋น๋์นญํค ์ํธํ๋ฅผ ํตํด์ ์๋ฒ์ ๋ณด๋ด๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
์ด๋ฌํ ๋ณด์์ ๋จ์ ์๋ ๋์นญํค ์ํธํ๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ๋น๋์นญํค ์ํธํ์ ๋นํด ์ฐ์ฐ ์๋๊ฐ ๋น ๋ฅด๋ค
๋ํ์ ์ธ ๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ผ๋ก๋
๋ฑ๋ฑ์ด ์๊ณ AES ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ์ ๋ํด ์ ๋ฆฌํด๋ณผ ์์ ์ด๋ค.
๋น๋์นญํค ์ํธํ๋ ์ํธํ์ ๋ณตํธํ์ ์๋ก ๋ค๋ฅธ ํค๋ฅผ ์ฌ์ฉํ๋ ์๊ณ ๋ฆฌ์ฆ์ ์๋ฏธํ๋ค
์์ ๊ทธ๋ฆผ์ ์์๋ก ๋ค๋ฉด
๊ณต๊ฐํค๋ฅผ ํตํด ์ํธํ๋ ๋ฐ์ดํฐ๋ ๊ฐ์ธํค๋ฅผ ํตํด์๋ง ๋ณตํธํ ํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ ๊ณผ์ ์ด ๊ฐ๋ฅํ๋ค
๊ณต๊ฐํค๋ ๋๊ตฌ๋ ์ป์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๊ตฌ๋ ์ํธํ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณตํธํ ํ ์ ์๊ฐ ๋๋ฌธ์ ๋ณด์์ ์ธ ์ธก๋ฉด์์๋ ๊ถ์ฅ๋์ง ์์ง๋ง ์ํธํ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณตํธํ ํ์ ๋ ์ด์ ๋ฐ์ดํฐ์ ๊ฐ์์ง๋ง ํ์ธํ๋ฉด ๋๋ ๋์งํธ ์๋ช
์์ ์ฌ์ฉ๋๋ค.
B์ ์ฅ์์ ๊ณต๊ฐํค๋ฅผ ํตํด์ ๋ณตํธํ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋ฉด A๊ฐ ํด๋น ๋ด์ฉ์ ๋ณด๋์์ด ๋ณด์ฅ๋๊ณ ๋ณตํธํ ํ ๋ฐ์ดํฐ์ ์๋ณธ์ด ๊ฐ์ผ๋ฉด ๋ฐ์ดํฐ์ ์์กฐ๊ฐ ์์์ด ๋ณด์ฅ๋๋ค.
์์ ์ดํด๋ณธ ๋์นญํค์ ๋นํด ๋ณด์์ ์ผ๋ก ์ฅ์ ์ด ์์ง๋ง ์ฐ์ฐ๋์ด ๋ง์ ๋๋ฆฌ๋ค๋ ๋จ์ ์ด ์๋ค.
๋ํ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก
๋ฑ๋ฑ์ด ์๊ณ RSA ์๊ณ ๋ฆฌ์ฆ์ ํตํ ์ํธํ-๋ณตํธํ ๊ณผ์ , ECDSA๋ฅผ ํตํ ์๋ช , ์๋ช ๊ฒ์ฆ ๊ณผ์ ์ ๊ตฌํํด๋ณผ ์์ ์ด๋ค.
์ง๊ธ๊น์ง ์์ผ ์ฐ๊ฒฐ์ ํด๋ณธ ํ๋ก์ ํธ๊ฐ ํ๋๋ฐ์ ์์ด ์ ํํ๊ฒ ๊ณต๋ถํ์ง ๋ชปํ๋๋ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ๋์ ๋ค๋ฅด๋ค.
์๋ก ๋ค๋ฅธ ์ปดํจํฐ๋ผ๋ฆฌ ์ํตํ๊ธฐ ์ํ ํ๋กํ ์ฝ
์๋ฐฉํฅ ํต์ ์ ์ํด ์น์์ผ ๊ธฐ์ ์ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋จํธ์ ์ผ๋ก ๋ดค์ ๋, ์ฐ๋ฆฌ๊ฐ Socket ์ฐ๊ฒฐ์ ํตํด ์ป์ผ๋ ค๋ ์ด์ ์ด(์๋ฐฉํฅ ํต์ , ์ค์๊ฐ ๋คํธ์ํน) ๋์ผํ๊ธฐ ๋๋ฌธ์ ๋น์ทํ๊ฒ ์๊ฐํ๋๊ฒ ๊ฐ๋ค.
์น์์ผ์ ์ฌ์ฉํ๊ธฐ ์ด์ ์ ์๋ฐฉํฅ๊ณผ ์ค์๊ฐ ๋คํธ์ํน์ ์ํด์๋ ์ผ์ ์ฃผ๊ธฐ๋ก API Call์ ์ด์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๋ Polling ๋ฐฉ์์ ์ฌ์ฉํ๋๋ฐ ๋จ์ํ๊ฒ ์๊ฐํด๋ด๋ ์๋ฒ์ ๋ถํ๊ฐ ์ฌํด์ง๋ค
์ด๋ฐ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด ๋์จ๊ฒ์ด WebSocket์ด๊ณ HTML5์ ๊ธฐ์ ์ด๊ธฐ ๋๋ฌธ์ ์ค๋๋ ๋ฒ์ ์ ์น ๋ธ๋ผ์ฐ์ ๋ ์ด๋ฅผ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์ Socket.io๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํ๋ค
Socket.io๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ js๋ฅผ ํตํด์๋ง ๊ฐ๋ฐ ํ ์ ์์ผ๋ฉฐ, ์๋ฒ๋ socket.io๋ฅผ ํด๋ผ์ด์ธํธ๋ socket.io-client ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ๋ง ํ๋ค.
๋ฐ๋ผ์ ์๋ฒ๊ฐ socket.io๋ฅผ ์ด์ฉํด์ ๊ฐ๋ฐ๋์ด ์์ง ์๋ค๋ฉด ํด๋ผ์ด์ธํธ ๋จ์์๋ ๋น์ฐํ socket.io-client๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
1๋ฒ๊ณผ ์ผ๋งฅ์ํตํ๋ ๋ถ๋ถ์ด๋ค. ๋ฌผ๋ก ์๋ฒ์์ socket.io๋ก ๋ง๋ค์์ ๋, ํด๋ผ์ด์ธํธ์์ URLSessionSocket์ด๋ StarStream์ ์ด์ฉํด์ ์์ผ ํต์ ์ ํ ์๋ ์์ง๋ง socket.io๋ง์ด ๊ฐ์ง๊ณ ์๋ ๋ธ๋ก๋์บ์คํ ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ด ์ ์์ ์ผ๋ก ๋์ํ ์ง๋ ๋ฏธ์ง์์ด๋ฉฐ socket.io๋ฅผ ์ฌ์ฉํ์ง ์๋ ํธ์ด ๊ตฌํ ๋์ด๋๋ ๋ ๋ฎ๋ค.
๋ฐ๋ผ์ ์๋ฒ์ ๋ง์ถฐ ํด๋ผ์ด์ธํธ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ ์์ํฌ๋ฅผ ์ ํํ๋ ๊ฒ์ด ์ ์ ํ๋ค.
Swift WebSockets: Starscream or URLSession in 2021?
StarScream๊ณผ URLSessionSocket์ ์์ ๊ธ์ ์ฝ์ด๋ณด๊ณ ์ ํํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
๊ฐ๋จํ๊ฒ ์์ฝํ๋ฉด URLSession์ Apple์์ ๊ณ์ํด์ ์ง์ํด์ฃผ๊ณ ์๊ณ Starscream์ ์ต๊ทผ ์ ๋ฐ์ดํธ๊ฐ ์๋ค
URLSessionSocket์ด iOS13๊ณผ ํจ๊ป ๋์จ ๊ธฐ์ ์ด๊ธฐ ๋๋ฌธ์ ์ต์๋ฒ์ ์ด iOS13 ์ด์์ด๋ผ๋ฉด URLSession์ ์ฌ์ฉํ๋ ๊ฒ๋ ๋์์ง ์์ ์ ํ์ด๋ค (๋ฌผ๋ก receive๋ฅผ ์ฌ๊ท์ ์ผ๋ก ํธ์ถํด์ผ ํ๋ ๋ถํธํจ ์กด์ฌ)
์ฌ์ค ping, pong์ด hand shake ๊ณผ์ ์ค ์ผ๋ถ๋ผ๊ณ ์๊ฐํ์๋๋ฐ ๋์ ์์ ๋ค๋ฅด๋ค.
์ผ๋ฐ์ ์ผ๋ก ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์์ผ์ผ๋ก ์ฐ๊ฒฐ๋๊ณ ๋ ์ดํ์ ๋ ์ฌ์ด์ ์๋ฌด๋ฐ ๋ฐ์ดํฐ ์ก, ์์ ์ด ์๋ ์ํ๊ฐ ๊ธธ์ด์ง๋ฉด ์๋ฒ์์ ํด๋ผ์ด์ธํธ์์ ์ฐ๊ฒฐ์ ๋์ด๋ธ๋ค (idle timeout)
์ด๋ฐ ์ฐ๊ฒฐ ์ข ๋ฃ๋ฅผ ๋ง๊ธฐ ์ํด์ ์๋ฒ or ํด๋ผ์ด์ธํธ์์ ์๋ก ping, pong์ ์ฃผ๊ณ ๋ฐ์ผ๋ฉฐ ์ฐ๊ฒฐ์ ํ์ธํ๋ค.
ํธ๋ ์์ดํฌ ๊ณผ์ ์ ์์ผ ํต์ ์ ๊ฒฐ๊ตญ TCP ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๊ณ ์๊ธฐ ๋๋ฌธ์ ์์ผ ์ฐ๊ฒฐ์ ์์ํ ๋์ ์ข ๋ฃ๋ ๋, ํธ๋ ์์ดํฌ๋ฅผ ํตํด์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ก,์์ ํ ์ค๋น๊ฐ ๋์ด์์์ ๋ณด์ฅํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
๋น๋์นญํค ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ํตํด์ ์ํธํ-๋ณตํธํ
, ์๋ช
์์ฑ ๋ฐ ๊ฒ์ฆ
๋ ๊ฐ์ง๋ฅผ ์ํํ ์ ์๋ค
์ง๋๋ฒ ๋์นญํค์ ๋น๋์นญํค์ ์ฐจ์ด์ ์ ๋ํด ์ ๋ฆฌํ๋ฉด์ ์๋ช ์์ฑ ๋ฐ ๊ฒ์ฆ์ ๋ํด ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋๋ฐ ํด๋น ๋ด์ฉ์ ๊ตฌํํ๋ฉด์ ๋ ์์ธํ๊ฒ ์ ๋ฆฌํด๋ณด์
์ง๋๋ฒ์๋ ์ค๋ช ํ์์ง๋ง ์ผ๋ฐ์ ์ผ๋ก Public Key๋ฅผ ํตํด์ ์ํธํ, Private Key๋ฅผ ํตํด์ ๋ณตํธํ๋ฅผ ์งํํ์ง๋ง ์๋ช ์์ฑ ๋ฐ ๊ฒ์ฆ๊ณผ์ ์์๋ ๋ฐ๋๋ค.
// MARK: - Signature Base64
/// Representation์ ํ์ฌ ์ํฉ์ ๋ง๊ฒ ์ ํ
/// ์ด๋ค ํ์
์ String์ ์์ฑํ ์ง๋ ์ํฉ์ ๋ง๊ฒ base64 or Hex String
func makeSignatureBase64(
plainString: String
) -> String? {
if let data = plainString.data(using: .utf8),
let sig = try? privateKey.signature(for: SHA256.hash(data: data)) {
return sig.Representation.base64EncodedString()
} else {
return nil
}
}
์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด ์๋ช ํ๋ ค๋ ๋ฉ์์ง๋ฅผ ๋ฐ์ดํฐ ํ์ ์ผ๋ก ๋ณ๊ฒฝํ๊ณ , ํด์ ํจ์๋ฅผ ์ ์ฉํ ๋ค ์๋ช ์ ๋ง๋ค์ด ๋ด๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
(Swift์์ Hash ํจ์๋ 256, 384, 512 ์ธ ๊ฐ์ง๋ฅผ ์ ๊ณตํ๊ณ ๋ฐ๋ก 256์ด ๋ํดํธ๋ก ์ค์ ๋์ด ์๋ค)
ํด๋ผ์ด์ธํธ์์ ์ ๊ฒฝ์จ์ค ๋ถ๋ถ์ ์๋ช ์ Validation ํ๋ ๊ณณ์์
์ ๋๋ฅผ ์ ๊ฒฝ์จ์ฃผ๋ฉด ๋๋ค.
์๋ช ์ ๊ฒ์ฆ ํ๋ ๊ณผ์ ์์ฒด๋
๋ ๊ฐ์ง๋ก ๊ต์ฅํ ๊ฐ๋จํ๊ณ ์ฌ์ง์ด P256.PublicKey์ ๋ฉ์๋๋ก ์ด๋ฏธ ๊ตฌํ๋์ด ์๋ค.
์ฐ๋ฆฌ์๊ฒ ๋ฌธ์ ๋
์๋ฒ๋ก ๋ถํฐ ๋ฐ์์จ ์์ ์ธ ๊ฐ์ง๋ฅผ Swift์ P256.PublicKey, P256.Signing.ECDSASignature ํ์ ์ผ๋ก ์ด๋ป๊ฒ ๋ณํํด์ผ ํ๋๊ฐ?์ด๋ค. ์ด๋ ์ํฉ๋ง๋ค ๋ค๋ฅผ๊ฒ ๊ฐ์๋ฐ ์๋ฒ์์ ๋ณด๋ด๋ ์ฃผ๋ ํ์ ์ Data ํ์ ์ผ๋ก ๋ณ๊ฒฝ์ํค๊ณ ์ด๋ฅผ ์ด์ฉํด PublicKey, Signing.ECDSASignature ํ์ ์ผ๋ก ์ด๊ธฐํ ํ๋ฉด ๋๋ค.
func validate(
publicKey: String,
plainString: String,
sig: String
) -> Bool {
// sigType: String to P256.Signing.ECDSASignature Type
// pubType: String to P256.Signing.PublicKey Type
let digest = SHA256.hash(data: plainData)
P256.Signing.PublicKey.isValidSignature(sigType, for: digest)
}
ECDSA๋ฅผ ํตํ ํค ์์ฑ, ์๋ช ์์ฑ ๋ฐ ๊ฒ์ฆ์ ๊ตฌํํด๋ณด์๋๋ฐ ์ฌ์ค ์ ๋ฆฌํ๊ณ ๋ณด๋ฉด ๊ต์ฅํ ๊ฐ๋จํ๋ฐ ์ต์ํ์ง ์์ ๋ด์ฉ์ด๋ผ ์๊ฐ์ด ์ข ๊ฑธ๋ ธ๋ค.
์ฌ์ค ์ ๋ฆฌํ ๋ด์ฉ์ด์ธ์ ๋น๋์นญํค์ Private Key๋ฅผ iOS์์ ์ด๋์ ์ ์ฅํ ๊ฒ์ธ๊ฐ? ์ ๋ํ ๊ณ ๋ฏผ์ ๋ง์ด ํ์๋๋ฐ ํค์ฒด์ธ ๋งํ ๊ณณ์ด ์๋๊ฒ ๊ฐ๋ค. ํค ์ ์ฅ ๊ด๋ จ์ ๋ํด์๋ ์ฐ์ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ๋ชจ๋ ์ ๋ฆฌํ๊ณ ๋ ์ดํ์ ์ ๋ฆฌํ ์์ ์ด๋ค.
์ด์ ๋ค์์๋ RSA๋ฅผ ํตํ ์ํธํ-๋ณตํธํ
๊ณผ์ ์ ๋ํด ์ ๋ฆฌํ๊ณ , AES๋ฅผ ํตํ ์ํธํ-๋ณตํธํ
๋ฅผ ์ ๋ฆฌํ ์์ ์ด๋ค.
AES(Advanced Encryption Standard)๋ ํ์ฌ ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋
๋์นญํค
์ํธํ ์๊ณ ๋ฆฌ์ฆ ์ค ํ๋์ ๋๋ค.
ECDSA, RSA ๊ฐ์ ๋น๋์นญํค ์๊ณ ๋ฆฌ์ฆ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฐ๋ฆฌ๋ AES๊ฐ ์ ํํ๊ฒ ์ด๋ค ์๊ณ ๋ฆฌ์ฆ์ธ์ง๊ฐ ์ค์ํ๊ฒ ์๋๋ค.
๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ด๋ฏ๋ก ์์ ๋๊ฐ์ง ์ ๋๋ง ๋จธ๋ฆฌ์์์ ๋ ์ค๋ฅด๋ฉด ๋๋ค.
์์๋๋ก ๊ตฌํํ๋ฉด์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํด๋ณด์.
์ ๋ฆฌํ๊ธฐ์ ์์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด CryptoKit
์ ์ด์ฉํด์ AES๋ฅผ ๊ตฌํํ๋ ๊ณผ์ ์ ์ ๋ฆฌํ ๊ฑด๋ฐ ๊ฐ์ธ์ ์ผ๋ก ์ถ์ฒํ์ง ์๋๋ค. CryptoKit์ ์ด์ฉํด ์ํธํ/๋ณตํธํ๋ฅผ ์งํํ๋ฉด AES.GCM.SealedBox
ํ์
์ด ํ์ฐ์ ์ผ๋ก ํ์ํ๊ฒ ๋๋๋ฐ ์๋ฒ์ ์คํ์ ๋ง์ถ๊ฑฐ๋ ์๋ฒ์ ํจ๊ป ์ํธํ/๋ณตํธํ๋ฅผ ์งํํ๊ธฐ ๋๋ฌด ์ด๋ ค์์ง๋ค.
๋ฐ๋ผ์ ๊ฐ๋ ์ ๋๋ง ์ตํ๊ณ ์๋ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด์ CommonCrypto๋ฅผ ์ด์ฉํ๊ฑฐ๋ CryptoSwift ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ ๊ทน ์ถ์ฒํ๋ค.
AES encryption in Swift
https://github.com/krzyzanowskim/CryptoSwift
AES๋ ์ฐ๋ฆฌ๊ฐ ์ด์ ์ ๋ดค๋ ๋น๋์นญํค ์๊ณ ๋ฆฌ์ฆ๊ณผ ๋ฌ๋ฆฌ ๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ด๊ธฐ ๋๋ฌธ์ ํ๋์ ๋น๋ฐํค๋ง ์์ฑํ๋ฉด ๋๋ค. ๊ทธ๋ฆฌ๊ณ IV(Initial Vector)์ ๋ฐ๋ผ ๊ฐ์ ํค๋ก ๊ฐ์ ํ๋ฌธ์ ์ํธํ ํ๋๋ผ๋ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง๊ฒ ๋๋ค.
๊ฒฐ๊ตญ ์ํธํ/๋ณตํธํ ๊ณผ์ ์ ์ํด์๋ ๋์นญํค, IV
๊ฐ ํ์ํ๋ค
final class AES256 {
private let dataStorage: KeyChainDataStorage
private var key: SymmetricKey?
private var iv: AES.GCM.Nonce?
init(
dataStorage: KeyChainDataStorage
) {
self.dataStorage = dataStorage
generateKey()
}
/// Make AES256 RandomKey, 16 Byte inital vector
private func generateKey() {
let refStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var randomStr = ""
for _ in 0..<16 {
randomStr += String(refStr.randomElement()!)
}
let randomIV: [UInt8] = Array(randomStr.utf8)
do {
self.key = SymmetricKey(size: .bits256)
self.iv = try AES.GCM.Nonce(data: randomIV)
} catch let err {
print("Make IV Error:", err.localizedDescription)
}
}
}
AES256์ ์ฌ์ฉํ๋ฉด 32Byte์ ๋๋คํ ํค, 16Byte์ ๋๋คํ IV๊ฐ ์์ฑ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก String ํ์ ํ๋๋น 1Byte์ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ 32, 16์ ๊ธธ์ด๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค.
์์ ์ฝ๋์์ ๊ตณ์ด String์ ๋ฒ์๋ฅผ 09, azA~Z๋ก ๋ ์ด์ ๋ utf8 ๋ณํ์ ํด๋น ๋ฒ์๊ฐ ์๋ ๊ฒฝ์ฐ ๊ฐ๋ณ์ ์ผ๋ก ํ ๊ธ์๋น 2Byte์ ํฌ๊ธฐ๋ฅผ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ์๋์ ์ผ๋ก ๊ธธ์ด๋ฅผ ์ ํํ๋ค.
/// AES - Encrypt plain string to rsa base64 String
func encryptByAES(
plainString: String
) -> AES.GCM.SealedBox? {
guard
let key = key,
let iv = iv,
let data = plainString.data(using: .utf8),
let sealedBox = try? AES.GCM.seal(data, using: key, nonce: iv)
else { return nil }
return sealedBox
}
Encryption์ ๊ฒฝ์ฐ ์์ ์์ฑํ ๋์นญํค์ iv๋ฅผ ํตํด์ ์ํธํ๋ฅผ ์งํํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ ์ํธํ ๊ฒฐ๊ณผ๋ก ๋์ค๋ Type์ด AES.GCM.SealedBox
๋ผ๋ ๊ฒ์ด๋ค.
/// AES - Decrypt rsa base64 String to plain String
func decryptByAES(
encryptedBox: AES.GCM.SealedBox
) -> String? {
guard
let key = key,
let sealedBox = try? AES.GCM.SealedBox(
nonce: encryptedBox.nonce,
ciphertext: encryptedBox.ciphertext,
tag: encryptedBox.tag
),
let decryptedData = try? AES.GCM.open(sealedBox, using: key)
else { return nil }
return String(data: decryptedData, encoding: .utf8)
}
๋ณตํธํ ๊ณผ์ ์์๋ ํ๋ผ๋ฏธํฐ ํ์
์ด AES.GCM.SealedBox
์ธ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ AES.GCM.SealedBox
๋ฅผ ์์ฑํ ๋, tag๊ฐ ๋ค์ด๊ฐ๋๋ฐ ์ด๋ Encryption์ ํตํด ์์ฑ๋ AES.GCM.SealedBox
์ tag์ ๋์ผํด์ผ ํ๋ค.
์ด๋ฐ ๋ฌธ์ ์ ๋๋ฌธ์ Encryption์ ๊ฒฐ๊ณผ๊ฐ์ String์ด ์๋ AES.GCM.SealedBox
๋ก ์ฌ์ฉํ๋ค.
ํด๋ผ์ด์ธํธ์์๋ง AES ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง ์๋ฒ์ ํต์ ํ๋ ๊ฒฝ์ฐ์๋ ์๋ฒ์AES.GCM.SealedBox
ํ์
์ ์ฃผ๊ณ ๋ฐ์ง ์๊ณ ์๋ ์๋ก ์ํธํ/๋ณตํธํ๊ฐ ๋ถ๊ฐ๋ฅํด์ง๊ธฐ ๋๋ฌธ์ ์น๋ช
์ ์ธ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค.
๋ฌผ๋ก ์ํฉ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ง๋ง ์ผ๋ฐ์ ์ผ๋ก๋ Base64String, HexString ํ์ ์ผ๋ก ์ํธํ ๊ฒฐ๊ณผ๊ฐ์ ์ฃผ๊ณ ๋ฐ๊ธฐ ๋๋ฌธ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๊ฒ์ ์ถ์ฒํ๋ค.
๋์นญํค์ ๊ฒฝ์ฐ ์๋ฒ์ ํค๋ฅผ ์ด๋ป๊ฒ ์์ ํ๊ฒ ๋ณด๋ผ๊ฒ์ธ๊ฐ? ๋ ๊ต์ฅํ ์ค์ํ ๋ฌธ์ ์ด๋ค.
๋ฌผ๋ก ํด๋ผ์ด์ธํธ ๊ฐ๋ฐ์ ํผ์์ ๊ณ ๋ฏผํ ๋ฌธ์ ๋ ์๋์ง๋ง ์ผ๋ฐ์ ์ผ๋ก๋ ๋์นญํค๋ฅผ ๋น๋์นญํค๋ฅผ ํตํด ์ํธํ ํ๊ณ ๋น๋์นญํค ๊ณต๊ฐํค๋ฅผ ํจ๊ป ๋ณด๋ด์ ์๋ฒ์์ ๋ณตํธํํด์ ํค๋ฅผ ์ป์ด๋ด๋ ๋ฐฉ์์ ๋ง์ด ์ฌ์ฉํ๊ณ ์๋๊ฒ ๊ฐ๋ค.
(์ฌ์ค ๊ทธ๊ฒ๋ฐ์ ์ํด๋ด)
ํ๋ก์ ํธ ๊ตฌํ์ฌํญ ์ค ์๋ฒ๋ก ๋ถํฐ ๋ฐ์ html
์ WebView๋ฅผ ํตํด์ ๋ณด์ฌ์ค์ผํ๋๋ฐ ์ด๋ ๊ฒช์๋ ์ด์๋ค์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก WKWebView๋ฅผ SwiftUI์์ ์ ๊ณตํด์ฃผ์ง ์๊ธฐ ๋๋ฌธ์ UIViewRepresentable ํ๋กํ ์ฝ์ ์ด์ฉํด์ ๊ตฌํํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฐ์ UIKit์ผ๋ก ๋จผ์ ๊ตฌํํด๋ณด๊ณ ์ ํ๋ค.
import UIKit
import WebKit
class ViewController: UIViewController {
let webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setupConstraints()
setupWebView()
}
func setupView() {
webView.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.addSubview(webView)
}
func setupConstraints() {
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
func setupWebView() {
let testHTML =
"""
... HTML ...
"""
webView.navigationDelegate = self
webView.loadHTMLString(testHTML, baseURL: nil)
}
}
์์ ๊ฐ์ด webView์ height constraints๋ฅผ ์ค์ ํ์ง ์๊ณ , html์ ๋ก๋ํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์ฌ์ค ์ฒ์์๋ top, leading, trailing constraints๋ง ์ค์ ํด์ฃผ๋ฉด height์ webView์์ loadํ html์ ์๋์ผ๋ก ๋จน์ง ์์๊น? ์๊ฐํ๋๋ฐ ์ด๋ฆผ๋ ์์๋ค.
์ค์ ๋ก ์ฒ์์ webView์ height์ ์ค์ ํ๋ฉด ํด๋น ํฌ๊ธฐ ๋งํผ์ WebView๋ง ์์ฑ๋๊ณ ๋ง์ฝ ์ค์ load๋๋ html์ด ํด๋น ํฌ๊ธฐ๋ณด๋ค ํฌ๋ค๋ฉด ์คํฌ๋กค ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์คํฌ๋กค ์์ด html์ด load ๋๋ ์๊ฐ์ ๋์ด๋ฅผ ๊ณ์ฐํด์ dynamic ํ๊ฒ height์ ์ค์ ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
๊ฒฐ๋ก ๋ถํฐ ์๊ธฐํ์๋ฉด, WKWebView Delegate ์ค ์ ํํ๊ฒ html์ด load๋๋ ์์ ์ ํธ์ถ๋๋ ๋ฉ์๋๋ ์กด์ฌํ์ง ์๋๋ค.
WKWebView์ด์ ์ UIWebView์๋ webViewDidFinishLoad
๋ผ๋ ๋ฉ์๋๋ฅผ ํตํด์ ๊ฐ๋ฅํ์ง๋ง ์ด์งธ์ ์ธ์ง WKWebView์๋ ์๋ค...
๊ทธ๋๋ ์คํ์ค๋ฒํ๋ก์ฐ์์ ์ด๋ป๊ฒ๋ ๊ตฌํํ ์ฌ๋๋ค์ ๋ฐฉ๋ฒ์ ์ด์ฉํด์ ์ผ๋งค(?)๋ก๋ผ๋ ์ด๋ฅผ ๊ตฌํํด๋ณด๊ณ ์ ํ๋ค.
์คํ ์ค๋ฒํ๋ก์ฐ
์์ ์ ์ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง๊ฐ ์กด์ฌํ๋ค.
์์ฝํ๋ฉด WKNavigationDelegate์ ์กด์ฌํ๋ didFinish ์์ ์ + ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ด์ฉํด scrollView์ content height์ ๊ณ์ฐํด ์ด๋ฅผ webView์ height์ผ๋ก ๋ฐ๊ฟ์น๊ธฐ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ฌ์ค ๋๋ฒ์งธ ๋ฐฉ๋ฒ์ js๋ฅผ ์ ๋ชจ๋ฅด๋ ๋๋ก์๋ ํด๋น ์คํฌ๋ฆฝํธ๊ฐ ์ ํํ ์ด๋ค ์์ ์ ํธ์ถ๋๋์ง ๋ชฐ๋ผ ์ ํํ ๋ฐฉ๋ฒ์ธ์ง ์์ง ํ๋จ์ ํ์ง ๋ชปํ๊ณ ,
์ฒซ๋ฒ์งธ ๋ฐฉ๋ฒ์ didFinish๊ฐ ํธ์ถ๋๋ ์์ ์ผ๋ก ๋ถํฐ n์ด ํ์๋ html load๊ฐ ์๋ฃ๋์์ ๊ฒ์ด๋ผ๋ ๋ง์ฐํ ๋ฏฟ์์ ๊ฐ์ง๊ณ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ธ์ ์ผ๋ก ๋ ๋ฐฉ๋ฒ ๋ชจ๋ ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์๋๋ผ๊ณ ์๊ฐํ๋ค. (๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ๋ฐฉ๋ฒ์ ์ฐพ๊ฒ ๋๋ฉด ๋ค์ ๋ ์ ๋ฆฌํ ์์ ์ด๋ค.)
extension ViewController: WKNavigationDelegate {
func webView(
_ webView: WKWebView,
didFinish navigation: WKNavigation!
) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self = self else { return }
NSLayoutConstraint.activate([
self.webView.heightAnchor.constraint(equalToConstant: self.webView.scrollView.contentSize.height)
])
}
}
}
์ฒซ๋ฒ์งธ ๋ฐฉ๋ฒ์ ์ด์ฉํด์ ๊ตฌํํ๋ฉด ์์ ๊ฐ๋ค. (0.3์ด ์์ load๊ฐ ์๋ฃ๋์ง ์๋ case๋ ์์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ ํ ์ ์๋ค)
์์ WebView๋ฅผ SwiftUI์์ ๊ตฌํํด๋ณด์
struct HtmlView: UIViewRepresentable {
let htmlContent: String
let webView = WKWebView()
@ObservedObject
var viewModel: HTMLStringViewModel
func makeUIView(context: Context) -> WKWebView {
webView.scrollView.isScrollEnabled = false
webView.scrollView.panGestureRecognizer.isEnabled = false
webView.scrollView.delegate = context.coordinator
webView.navigationDelegate = context.coordinator
webView.isUserInteractionEnabled = false
webView.loadHTMLString(htmlContent, baseURL: nil)
return webView
}
func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<HtmlView>) {
guard context.coordinator.webView == nil else { return }
context.coordinator.webView = uiView
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject {
var webView: WKWebView?
var parent: HtmlView
init(parent: HtmlView) {
self.parent = parent
}
}
}
extension HtmlView.Coordinator: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return nil
}
}
extension HtmlView.Coordinator: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self = self else { return }
self.parent.viewModel.heightSubject.send(webView.scrollView.contentSize.height)
}
}
}
final class HtmlViewModel: ObservableObject {
let heightSubject: PassthroughSubject<CGFloat, Never>
init(_ sub: PassthroughSubject<CGFloat, Never>) {
self.heightSubject = sub
}
}
SwiftUI์์๋ View๊ฐ ๊ตฌ์กฐ์ฒด ํ์ ์ด๊ธฐ ๋๋ฌธ์ Delegate ํจํด์ Coordinator๋ฅผ ํตํด ์ฑํํ๋ค๋ ๊ฒ๋ง ์ ์ธํ๋ฉด ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ๊ณผ์ ์ ์ฌ์ค UIKit๊ณผ ์์ ํ ๋์ผํ๋ค.
์์ ๋ด์ฉ์ ๊ตฌํํ ๋, HtmlView๋ฅผ ์ ์ธํ๋ ๋ถ๋ถ์์ .frame() modifier๋ฅผ ํตํด์ ๋์ด๋ฅผ dynamicํ๊ฒ ์ค์ ํ๊ธฐ ์ํด์ Subject๋ฅผ ์ฌ์ฉํ๋ค.
๋ง๋ก๋ง ์ค๋ช
ํ๋ฉด ๋๊ฒ ํท๊ฐ๋ฆด ์ ์์ผ๋ ์ฝ๋์ ํจ๊ป ํ์ธํด๋ณด์
// View ๋ด๋ถ
HtmlView(
htmlContent: ---html---,
viewModel: HtmlViewModel(viewModel.input.---sub)
)
.frame(height: viewModel.output.testHeight)
// View์ ViewModel ๋ด๋ถ
input.--tSub
.sink(receiveValue: { [weak self] in
guard let self = self else { return }
self.output.testHeight = $0
})
.store(in: &cancellables)
์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ๋ค.
์ฌ์ ํ n์ด ์ด๋ด์ html์ด load ๋์ง ์๋ webView์ ๋ํด์๋ ๋ถ๊ฐ๋ฅ ํ๋ค๋ ํฐ ๋ฌธ์ ๊ฐ ๋จ์ ์์ง๋ง, ํด๋น ์ผ์ด์ค๋ฅผ ์ ์ธํ๋ฉด ๋ชจ๋ ์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. (์ค์ ํ๋ก์ ํธ์์ webView๊ฐ ๊ทธ๋ฆฌ๋ ์์ญ์ด ๊ต์ฅํ ์๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง ์ธ์ ๋ ์ง ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค.)
์ฐ์ , ์์ ๊ฐ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋๋ฐ ์ฌ์ ํ ์ฐ์ฐํ ๋ถ๋ถ๋ค์ด ๋จ์์๋ค. ํ์ง๋ง ์์ง๊น์ง ์๋ฒฝํ ๋ฐฉ๋ฒ์ ์ฐพ์ง ๋ชปํ ์ํ์ด๋ค ใ
ํน์๋ผ๋ ๋ ์ข์ ๋ฐฉ๋ฒ์ด ๋ ์ค๋ฅด๊ฑฐ๋ ์ฐพ๊ฒ ๋๋ฉด ๋ค์ ์ ๋ฆฌํ ์์ ์ด๋ค
์ด ๊ธ์ ๋ณด๋ค ๋ ์ข์ ์์ด๋์ด๊ฐ ์์ผ์ ๋ถ๋ค์ [email protected]์ผ๋ก ์ฐ๋ฝ์ฃผ์๋ฉด ์ ๋ง ์ง์ง ์ ๋ง ์ง์ง ๊ฐ์ฌํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์คํ ๋ฆฌ๋ณด๋์์ ์ฌ๋ฌ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ค์ ๋ง๋ค์ด์ ์ฑ์ ํ๋ฉด๊ณผ ์ ์ฒด์ ์ธ ํ๋ก์ฐ๋ฅผ ๊ตฌ์ฑํ๊ณ table view cell, collection view cell, custom view๋ค์ xibํ์ผ๋ก ๋ง๋ค์ด์ ์ฌ์ฉํ๋๋ฐ ์ด ๋์ ์ ํํ๊ฒ ์ด๋ค ์ฐจ์ด๊ฐ ์๋ ๊ฒ์ผ๊น?
์ฐ์ ์ปดํ์ผ ์ดํ ์์ฑ๋๋ ํ์ผ์ ํ์ธํด๋ณด๋ฉด xib ํ์ผ์ nib
์ผ๋ก ์คํ ๋ฆฌ๋ณด๋๋ stroyboardc
๋ก ๋ณ๊ฒฝ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
storyboardc
ํ์ผ์ ์์ฑ๋ ์ ์๋ฌธ์ nibํ์ผ๋ค์ ๋ฌด์์ผ๊น?
<viewController storyboardIdentifier="Test" id="hef-Go-4NS" customClass="TestViewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<viewController id="BYZ-38-t0r" customClass="ViewController">
์ํ๊น๊ฒ๋ nibํ์ผ์ ์ด์ด๋ณด๋๊ฑด ์ ๋ก ํ๋ก๊ทธ๋จ์ด๋ผ ํ์ธ์ ํด๋ณด์ง ๋ชปํ์ง๋ง ์คํ ๋ฆฌ๋ณด๋ ํ์ผ์ ์์ค์ฝ๋๋ก ํ์ธํด๋ณด๋ฉด viewcontroller์ view์ ID๋ก ํ์ผ๋ช ์ด ์์ฑ๋๊ฒ์ ํ์ธํ ์ ์๋ค.
storyboardc ํ์ผ์ ํจ๊ป ์์ฑ๋๋ info.plist ํ์ผ์ ํ์ธํด๋ณด๋ฉด ์๋ 3๊ฐ์ ํ์ผ๋ช ์ดUIViewControllerIdentifiersToNibNames์ธ ๊ฒ์ ํ์ธํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋น value ๊ฐ์ storyboard ID์ ๊ฐ๊ณผ ๋์ผํ๊ฒ๋ ํ์ธํ ์ ์๋ค.
@IBAction func mainButtonTapped(_ sender: UIButton) {
print("Touch Event")
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: StaticTableViewController.storyboardID)
navigationController?.pushViewController(vc, animated: true)
}
๊ทธ๋ผ ์ด์ ์คํ ๋ฆฌ๋ณด๋์์ Segue๊ฐ ์๋ navigation push๋ก ๊ตฌํํ ๋ identifier๋ฅผ ์ค์ ํด์ผ ํ๋ ์ด์ ๋ฅผ ๋ช ํํ ์ ์ ์๋ค!
// SB
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB">
// Xib
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB">
์คํ ๋ฆฌ๋ณด๋์ xibํ์ผ์ ๋ชจ๋ ์์ค์ฝ๋๋ก ํ์ธํด๋ณด๋ฉด xml๋ก ํํ๋ xibํ์ผ์์ ์ ์ ์๋ค.
๊ทธ๋ผ ์คํ ๋ฆฌ๋ณด๋์ xib๋ ๋ฌด์์ด ๋ค๋ฅธ๊ฑธ๊น?
์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก xibํ์ผ๋ก ์์ฑํ๋ cell, custom view๋ค์ ํ๋์ ๋ทฐ ์ ๋ณด๋ง ํฌํจํ๊ณ ์๋ ๋ฐ๋ฉด ์คํ ๋ฆฌ๋ณด๋๋ ์ฌ๋ฌ ๋ทฐ ์ปจํธ๋กค๋ฌ xibํ์ผ๋ค๊ณผ ๋ทฐ ๊ด๊ณ์ ๋ณด๋ค์ ํฌํจํ๊ณ ์๋ ํ์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ค
ํ ์ด ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ ํ์ฌ ์ฝ๋์ ๋ฅ๋งํฌ๋ฅผ ์ ์ฉํ๋ ๊ณผ์ ์ค์ ๊ฒช์ ๋ฒ๊ทธ๋ฅผ ์ ๋ฆฌํ๊ณ ์ ํ๋ค.
(์ฌ์ค ๊ฑฐ์ ํ๋ฌ ์ ๋ ์ง๋๊ฒ ๊ฐ์๋ฐ ๋๋ฌด ์ ์ ์์ด ๋ฐ๋นด๋ค.)
๊ฒฐ๋ก ๋ถํฐ ์๊ธฐํ์๋ฉด SwiftUI ๊ธฐ๋ฐ์ด ์๋ UIKit ๊ธฐ๋ฐ์ RootView๋ฅผ ๋ฐ๊พธ๋ ๋์์ด ๋ฌธ์ ์๋ค.
let window = UIApplication.shared
.connectedScenes
.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
.first { $0.isKeyWindow }
guard let window = window else { return }
let tabViewModel = TestTabViewModel()
window.rootViewController = UIHostingController(
rootView: TestTabView().environmentObject(tabViewModel)
)
window.makeKeyAndVisible()
UIView.transition(
with: window,
duration: 0.5,
options: .transitionCrossDissolve,
animations: nil,
completion: nil
)
๊ธฐ์กด์๋ ํน์ ํ๋ก์ธ์ค๊ฐ ๋๋๋ฉด ์์ ๊ฐ์ ๋ฐฉ์์ผ๋ก RootView๋ฅผ ๋ฐ๊ฟ์ฃผ๋ ๋ฐฉ์์ ์ด์ฉํด์ ํ๋ฉด ์ ํ์ ํ๊ณ ์์๋ค.
์ ํํ๊ฒ ์ ๋ฆฌํ๋ฉด
์์ 3๊ฐ์ง ๋ก์ง์ ํ๋ฉด์ ์ฑ๊ณต ์ ๋ฌด์ ๋ฐ๋ผ ์์ ํ๋ ํ๋ฉด์ ์คํ๋์ฌ ๋ทฐ์์ UIKit ๊ธฐ๋ฐ์ RootView๋ฅผ ๋ฐ๊ฟ์ฃผ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๊ณ ์์๋ค.
ํ์ง๋ง ์์ ๊ฐ์ ๋ฐฉ์์ผ๋ก๋ ์ฐ๋ฆฌ๊ฐ ํ ์ด ํ๋ก์ ํธ์์ ํ์ธํ๋ .openURL ์ด ๋์ํ์ง ์๋๋ค ใ
ใ
์ฌ์ค ์ ํํ ์ด์ ์ ํจ๊ป ์ ๋ฆฌํ๊ณ ์ถ์๋๋ฐ ๋ฐ๋ฆฐ ์ผ์ด ์ฐ๋๋ฏธ๋ผ ์ฐ์ ํด๊ฒฐ๋ฐฉ๋ฒ์ด๋ผ๋ ์ ๋ฆฌํด๋๊ณ ์ ํ๋ค.
struct AppRootView: View {
@StateObject
var viewModel = AppRootViewModel()
var body: some View {
Group {
switch viewModel.nextView {
case .splash:
SplashView()
case .main:
TodoTabView()
}
}
}
}
final class AppRootViewModel: ObservableObject {
enum NextView {
case splash
case main
// case login
}
@Published var nextView: NextView = .splash
init() {
fetchInitInfo()
}
}
extension AppRootViewModel {
// BL์ ๋ฐ๋ผ ์ฒ๋ฆฌ
func fetchInitInfo() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
guard let self = self else { return }
self.nextView = .main
}
}
}
ํ ์ด ํ๋ก์ ํธ๋ผ Lottie ์ ๋๋ฉ์ด์
์ด๋ ๋ทฐ๋ชจ๋ธ์ ๋น์ฆ๋์ค ๋ก์ง์ ์ ๋ถ ์๋ตํ๋ค.
๊ธฐ์กด์ RootViewCon์ ๋ฐ๊พธ๋ ๋ก์ง์ ๊ฑท์ด๋ด๊ณ AppRootView๋ฅผ ๋ง๋ค์ด์ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ nextView๋ฅผ ๋ฐ๊ฟ์ฃผ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
์ง๋ ์๊ฐ๊น์ง TCP/IP, UDP์ ๊ฐ์ ๋ฐ์ดํฐ ์ ์ก ํ๋กํ ์ฝ์ ๋ํด์ ๊ณต๋ถํ๋๋ฐ HTTP์ ๋ํ ๋ด์ฉ์ ์ ๋ฆฌํ๊ธฐ ์ด์ ์ ๋์ ์ฐจ์ด๋ฅผ ๋ช ํํ ์ง๊ณ ๋์ด๊ฐ๊ณ ์ ํ๋ค.
๊ฒฐ๋ก ๋ถํฐ ๋งํ์๋ฉด HTTP๋ TCP/IP ๋ณด๋ค ์์ ๊ฐ๋ ์ด๋ค.
TCP/IP 4๊ณ์ธต ๊ด์ ์์ ๋ฐ๋ผ๋ณด๋ฉด HTTP๋ ์์ฉ๊ณ์ธต์ ์ํ๊ณ TCP,UDP๋ ์ ์ก๊ณ์ธต, IP๋ ์ธํฐ๋ท ๊ณ์ธต์ ์ํด์๋๊ฒ์ ํ์ธํ ์ ์๋ค.
HTTP์ ๊ธฐ๋ฐ ํ๋กํ ์ฝ์ด TCP๋ผ๋ ์๋ฏธ๋ ์์ ๊ฐ์ด HTTP ํต์ ์ ์ผ๋ จ์ ๊ณผ์ ์ TCP๋ฅผ ์ฌ์ฉํ๋ค๋ ์๋ฏธ๋ผ๊ณ ์ดํดํ๋ค. (๋ฌผ๋ก UDP๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค)
โ ํด๋น ๋ด์ฉ ๊ด๋ จํด์๋ ๊ณต๋ถํ๋ฉด์ ๊ณ์ ์ถ๊ฐํ ์์ ์ ๋๋ค.
์ธํฐ๋ท์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ํ๋กํ ์ฝ๋ก ๊ฑฐ์ ๋ชจ๋ ํํ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๊ณ Web์์๋ ๋๋ถ๋ถ HTTP ํ๋กํ ์ฝ์ ์ฌ์ฉํ๊ณ ์๋ค.
(๊ตฌ๊ธ์์ F12๋ฅผ ํตํด์ HTTP ๋ฒ์ ์ ํ์ธํ ์ ์์)
๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋ฉด ์์ ๋ด์ฉ์ด๊ณ HTTP์ ํน์ง์ ๋ํด์ ์ฐจ๋ก๋๋ก ํ๋์ฉ ์ ๋ฆฌํด๋ณด์.
์๋ฒ์์ ์ฑ์ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๊ณ ํด๋ผ์ด์ธํธ(์ฑ)๋ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐ๋ผ UI๋ฅผ ๊ทธ๋ ค์ค๋ค.
ํด๋ผ์ด์ธํธ-์๋ฒ ๊ตฌ์กฐ๊ฐ ๊ณ ๋ํ ๋๋ฉด ์์ ๊ฐ์ด ํด๋ผ์ด์ธํธ์ ์๋ฒ์ ์ญํ ๋ถ๋ฆฌ๋ฅผ ํตํด์ ๋ ๋ฆฝ์ ์ผ๋ก ๋ฐ์ ํ ์ ์๋ค. (์ฌ์ง์ด ๋ชจ๋ฐ์ผ์ ์น๊ณผ ๋ฌ๋ฆฌ ๊ตฌ๊ธ์ด๋ ์ ํ์ ์ฌ์ฌ๊ณผ์ ๋ ์๊ธฐ ๋๋ฌธ์ ๋น์ฆ๋์ค ๋ก์ง์ ์๋ฒ์์ ์ฒ๋ฆฌํด์ฃผ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋๋ผ๋ ์ฌ์ฌ ์์ด ์๋ฒ ์์ ๋ง์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.)
์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ์ํ๋ฅผ ๋ณด์กดํ์ง ์๋๋ค.
ํด๋น ๋ฌธ์ฅ๋ง ๋ณด๊ณ ์ดํดํ๊ธฐ๋ ์ด๋ ค์ฐ๋๊น ์์๋ฅผ ๋ค์ด์ ์ดํดํด๋ณด์.
/// ์ ์์ด ๋ฐ๋์ง ์๋ ๊ฒฝ์ฐ
๊ณ ๊ฐ: ๋
ธํธ๋ถ ์ผ๋ง์์?
์ ์(A): 380๋ง์์ด์
๊ณ ๊ฐ: 1๊ฐ ์ฃผ์ธ์
์ ์(A): ์ผ์๋ถ์ธ๊ฐ์? ํ ๋ถ์ธ๊ฐ์?
๊ณ ๊ฐ: 36๊ฐ์ ๋ฌด์ด์ ํ ๋ถ์
์ ์(A): ๋ค
์ ์์ด ์ด์ ๊ณ ๊ฐ์ ์์ฒญ์ ๋ํ ์ํ๋ฅผ ์๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ ๋ํ์ด๋ค. ๋ง์ฝ ์ ์์ด ์ค๊ฐ์ค๊ฐ ๋ฐ๋๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
/// ์ ์์ด ๋ฐ๋๋ ๊ฒฝ์ฐ
๊ณ ๊ฐ: ๋
ธํธ๋ถ ์ผ๋ง์์?
์ ์(A): 380๋ง์์ด์
๊ณ ๊ฐ: 1๊ฐ ์ฃผ์ธ์
์ ์(B): ..์? ๋ญ 1๊ฐ ๊ตฌ๋งคํ์๋ ๊ฑด๊ฐ์?
๊ณ ๊ฐ: 36๊ฐ์ ๋ฌด์ด์ ํ ๋ถ์
์ ์(C): ...์? ๋ฌด์จ ์ ํ ๋ช๊ฐ๋ฅผ 36๊ฐ์ ํ ๋ถ๋ก ๊ตฌ๋งคํ์๋๊ฑด๊ฐ์?
์ ์์ด ์ด์ ์ํ๋ค์ ๋ํ ์ ๋ณด๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ ์์ ์ธ ๋ํ๊ฐ ๋ถ๊ฐ๋ฅ ํ๊ณ ์ด์ ์ํ์๋ค์ ๋ํ ์ ๋ณด๋ฅผ ๋ค์ ๋ฌผ์ด์ผ ๊ณ ๊ฐ์ ์๊ตฌ๋ฅผ ๋ค์ด์ค ์ ์๋ค.
/// ์ ์์ด ๋ฐ๋์ง ์๋ ๊ฒฝ์ฐ
๊ณ ๊ฐ: ๋
ธํธ๋ถ ์ผ๋ง์์?
์ ์(A): 380๋ง์์ด์
๊ณ ๊ฐ: ๋
ธํธ๋ถ 1๊ฐ ์ฃผ์ธ์
์ ์(A): ์ผ์๋ถ์ธ๊ฐ์? ํ ๋ถ์ธ๊ฐ์?
๊ณ ๊ฐ: ๋
ธํธ๋ถ 1๊ฐ 36๊ฐ์ ๋ฌด์ด์ ํ ๋ถ์
์ ์(A): ๋ค
/// ์ ์์ด ๋ฐ๋๋ ๊ฒฝ์ฐ
๊ณ ๊ฐ: ๋
ธํธ๋ถ ์ผ๋ง์์?
์ ์(A): 380๋ง์์ด์
๊ณ ๊ฐ: ๋
ธํธ๋ถ 1๊ฐ ์ฃผ์ธ์
์ ์(B): ์ผ์๋ถ์ธ๊ฐ์? ํ ๋ถ์ธ๊ฐ์?
๊ณ ๊ฐ: ๋
ธํธ๋ถ 1๊ฐ 36๊ฐ์ ๋ฌด์ด์ ํ ๋ถ์
์ ์(C): ๋ค
๋ฌด์ํ์ธ ๊ฒฝ์ฐ์๋ ์ค๊ฐ์ ๋ค๋ฅธ ์ ์์ผ๋ก ๋ฐ๋๋๋ผ๋ ๊ณ ๊ฐ์ด ์ด์ ์ํ์ ๋ํ ์ ๋ณด๊ณผ ํจ๊ป ์์ฒญํ๊ธฐ ๋๋ฌธ์ ๊ณ ๊ฐ์ ์๊ตฌ๋ฅผ ๋ค์ด ์ค ์ ์๋ค.
์ด์ ๊ณ ๊ฐ๊ณผ ์ ์์ด ์๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ๋ก ์๊ฐํด๋ณด์.
HTTP๋ ์์ฒญ์ ์ฃผ๊ณ ๋ฐ์ ๋๋ง ์ฐ๊ฒฐ์ ์ ์งํ๊ณ ์๋ต์ ์ฃผ๊ณ ๋ฐ์ ์ดํ์๋ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ค.
CPU์์ N์ฝ์ด์ M์ค๋ ๋๋ ๋ฌผ๋ฆฌ์ ์ธ ์ฝ์ด์ ๊ฐ์์ ๋ ผ๋ฆฌ์ ์ธ ์ฝ์ด์ ๊ฐ์๋ฅผ ์๋ฏธํ๋ค
๋ชจ๋ ํ๋ก๊ทธ๋จ์ด ์คํ๋๊ธฐ ์ํด์๋ ๊ฒฐ๊ตญ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ผ์ CPU ์์์ ํตํด ์ฐ์ฐ์์ ์ด ํ์ํ๋ค.
ํ์ง๋ง ๊ณ ์ ์ ์ธ CPU๋ ์ด๋ฐ ์ฐ์ฐ์์
์ ์ํํ ์ ์๋๊ฒ ํ๋๋ฐ์ ์์์ง๋ง ์ต๊ทผ์๋ ์ฝ์ด
์ ๋ฑ์ฅ์ผ๋ก ์ด๋ฐ ๋ฌผ๋ฆฌ์ ์ธ ์ฐ์ฐ์ ์ํํ ์ ์๋ ์ ๋์ด CPU๋ด๋ถ์ ์ฌ๋ฌ๊ฐ ์กด์ฌํ๊ฒ ๋์๋ค.
์ฝ์ด์ ๋ฑ์ฅ์ ๋ฐ๋ผ์ ์ฌ๋ฌ ํ๋ก์ธ์ค์ ์์
์ ๋์์ ์ฒ๋ฆฌํ๋ ๋ณ๋ ฌ์ฒ๋ฆฌ
๊ฐ ๊ฐ๋ฅํ๊ฒ ๋์๋ค.
๊ฒฐ๋ก ๋ถํฐ ์ด์ผ๊ธฐ ํ๋ฉด, ์ด๋ ์ค๋ ๋๋ OS๊ฐ ์ธ์ํ๋ ๋
ผ๋ฆฌ์ ์ธ ์ฝ์ด
๋ฅผ ์๋ฏธํ๋ค.
์์๋ฅผ ํตํด์ ์ค๋ ๋์ ๊ฐ๋ ๊ณผ ํ์ดํผ ์ฐ๋ ๋ฉ์ ์ดํด ํด๋ณด์.
์ฝ์ด์ ๋ฑ์ฅ์ผ๋ก ์ฝ์ด์ ๊ฐ์ ๋งํผ ํ๋ก์ธ์ค์ ์์ ์ ๋์ ์ฒ๋ฆฌํ๋ ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ก๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ด๋ฐ ์ฝ์ด์ ๊ฐ์๊ฐ ๋ง์์ง๋ฉด ์๋๊ฐ ์ฆ๊ฐํ๋ ๊ฒ์ ๊ธฐ๋ํ ์ ์๋ค. (ํญ์ ๊ทธ๋ ์ง ์์ต๋๋ค..)
๊ทธ๋ผ ์ฌ๊ธฐ์ ํ์ ๋ ์์์ผ๋ก ์ฝ์ด์ ๊ฐ์๋ฅผ ๋๋ฆด ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น?
์ฌ๊ธฐ์ ๋ฑ์ฅํ๋ ๊ธฐ์ ์ด ํ์ดํผ์ฐ๋ ๋ฉ
์ด๋ค.
ํ์ดํผ ์ฐ๋ ๋ฉ์ด ์ ํํ๊ฒ ์ด๋ค ๊ฐ๋
์ผ๋ก ๋์ํ๋์ง๊น์ง๋ ์ฐพ์๋ณด์ง ๋ชปํ์ง๋ง
CPU๊ฐ ํ์ดํผ ์ฐ๋ ๋ฉ ๊ธฐ์ ์ ์ด์ฉํด์ OS์๊ฒ ์ฝ์ด์ ๊ฐ์๋ฅผ ์์ธ๋ค ์ ๋๋ก ์ดํดํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
์ฆ 4์ฝ์ด 8์ค๋ ๋์ cpu๋ ์ค์ ๋ฌผ๋ฆฌ์ ์ผ๋ก๋ 4๊ฐ์ ์ฝ์ด๋ฅผ ๊ฐ์ง๊ณ ์์ง๋ง OS์์์๋ 8๊ฐ์ ๋ ผ๋ฆฌ์ ์ฝ์ด๋ฅผ ๊ฐ์ง๊ฒ ๋์ด์ 8๊ฐ์ ์์ ์ ๋ณ๋ ฌ์ ์ผ๋ก ์คํํ ์ ์์์ ์๋ฏธํ๋ค.
ํด๋น ๋ด์ฉ๊ณผ ๋ณ๊ฐ๋ก ๊ฐ์ธ์ ์ผ๋ก ์ค๋ ๋๋ผ๋ ์ฉ์ด๋ฅผ ๋๋ฌด ๋ณตํฉ์ ์ผ๋ก ์ฌ์ฉํ๊ณ ์์ด์ ๊ฐ๋ ์ ์ก๊ธฐ ์ด๋ ค์ ์๋๋ฐ ํ๋์จ์ด์ ์ธ ์ค๋ ๋์ ์ํํธ์จ์ด์ ์ธ ์ค๋ ๋๋ฅผ ๊ตฌ๋ถํด์ ์ดํดํ ํ์๊ฐ ์๋ค.
RSA๋ ์ง๋๋ฒ์ ํ์ธํ๋ ECDSA์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋น๋์นญํค ์ํธํ์ ํ ์ ํ์ด๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ํธํ์ ๋์งํธ ์๋ช ์ ์ฌ์ฉ๋ ์ ์๋๋ฐ ECDSA๋ฅผ ํตํด์ ๋์งํธ ์๋ช ๊ณผ์ ์ ๊ตฌํํด๋ณด์๊ธฐ ๋๋ฌธ์ ์ด๋ฒ์๋ RSA๋ฅผ ํตํด์ ์ํธํ, ๋ณตํธํ ๊ณผ์ ์ ํ๋ฒ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค.
์์๋ก ๊ตฌํํ๋ฉด์ ๋ด์ฉ์ ์ ๋ฆฌํ ์์ ์ด๋ค
RSA๊ฐ ๋ญ์ง๋ ๋ชจ๋ฅด๋๋ฐ ์ด๋ป๊ฒ ๋ง๋๋์?
ECDSA์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ํ์์ ์ ๊ณตํด์ฃผ๊ณ ์๋ค. ์ฐ๋ฆฌ๋ ๋น๋์นญํค ์ํธํ์ ๋ํ ํน์ฑ๋ง ์๊ณ ์๋ค๋ฉด ์ ํ ๋ฌธ์ ์๋ค.
import Foundation
final class RSA {
private var privateKey: SecKey?
private var publicKey: SecKey?
private let dataStorage: KeyChainDataStorage
init(
dataStorage: KeyChainDataStorage
) {
self.dataStorage = dataStorage
if let privateKey = dataStorage.read(key: .rsa),
let secKey = stringToSecKey(privateKey) {
print("From KeyChain")
self.privateKey = secKey
self.publicKey = SecKeyCopyPublicKey(secKey)
} else {
print("From New")
generateRSAKeyPair()
saveRSAKey()
}
}
// FIXME: - Key Representation์ ์ค์ ํ ์ ์๋ ๋ฌธ์ ์
private func generateRSAKeyPair() {
var error: Unmanaged<CFError>?
let parameters: [CFString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 2048,
kSecAttrKeyClass: kSecAttrKeyClassPrivate
]
guard
let privateKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error),
let publicKey = SecKeyCopyPublicKey(privateKey)
else { return }
self.privateKey = privateKey
self.publicKey = publicKey
}
}
KeyChainDataStorage๋ ์ถํ ํค์ฒด์ธ ์ ์ฅ์์ ๋ค๋ฃจ๊ธฐ๋ก ํ๊ณ ํค ์์ฑ์์ ํ์ธํด์ผ ํ ๋ถ๋ถ์
ECDSA ์ํธํ ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ์ ๊ฒฝ์ฐ, CryptoKit์ ์ด์ฉํ์ง๋ง RSA๋ Security๋ฅผ ์ด์ฉํ๋ค.
SecKey ํ์ ๋ Security์ ์ ์๋ ํด๋์ค๋ก ์ ํํ ๊ตฌํ์ฒด๋ ํ์ธํ ์ ์์ง๋ง ์ํธํ ํค๋ฅผ ๋ํ๋ด๋๋ฐ ์ฌ์ฉ๋๊ณ ์ด๋ฅผ ์ด์ฉํด์ ์ํธํ, ๋ณตํธํ, ์๋ช ๋ค์ ๊ตฌํํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ฝ๋๋ค์ CF๊ฐ ๋ถ์ ํ์ ๋ค์ด ๋ง์๋ฐ Core Foundation์์ ์ ๊ณตํ๊ณ ์๋ ๋ฐ์ดํฐ ํ์ ์ผ๋ก Swift Type๊ณผ ํจ๊ป ์ฌ์ฉํ๊ธฐ ์ํด์๋ ํ์ ์บ์คํ ์ด ํ์ํ๋ค
ํค ์์ฑ ๊ณผ์ ์ ํ์ธํด๋ณด๋ฉด ์ฐ๋ฆฌ๊ฐ ์ํ๋ RSA Private Key ์คํ์ CFDictionary์ ๋ด์์ Private Key๋ฅผ ๋ง๋ค๊ณ Private Key๋ฅผ ํตํด์ Public Key๋ฅผ ์์ฑํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ณต์ ํํ์ด์ง๋ ์คํ์ค๋ฒ ํ๋ก์ฐ๋ฅผ ๋ง์ด ๋ค์ก์ง๋ง ์์ฑํ๋ ์์ ์ Representation์ ์ค์ ํ๋ ๊ฒ์ ๋ฐ๋ก ์ฐพ์ง ๋ชปํ๋ค.
์์ ์ ์์ต๋๋ค. ์ฐพ์ผ๋ฉด ์์ ํ๊ฒ ์ต๋๋ค!
์ฌ์ค ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ๋จ์ ๊ตฌํ ๋ณด๋ค๋ ์๋ฒ์ ์คํ์ ๋ง์ถ๋ ๊ณผ์ ์ด ์ค์ํ๋ฐ ์์ฑ์๋ฅผ ์ฐพ์ง ๋ชปํด์ ๊ฐ์ธ์ ์ผ๋ก ๊ต์ฅํ ์์ฌ์ ๋ค.
https://github.com/TakeScoop/SwiftyRSA
์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด Representation ์ค์ ๋ฟ๋ง ์๋๋ผ encrypt/decrypt ๊ณผ์ ์ swift์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ์ง ์๋ ํจ๋ฉ ๋ชจ๋๋ค๋ ์ ๊ณตํ๊ณ ์์ผ๋ ํ์์ ๋ฐ๋ผ์ ์ฌ์ฉํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
1ํธ์ ์ด์ด์ ๋๋ฒ์งธ ์ ๋ฆฌ๊ธ์ด๋ค!
์ฌ์ค ๊ฐ์๋ฅผ ๋น ๋ฅด๊ฒ ๋ค ๋ฃ๊ณ ์ ๋ฆฌํ๋ฉด์ ๋ณต์ตํ๋ ๋๋์ผ๋ก ํ๋ ค๊ณ ํ๋๋ฐ ์๊ฐ๋ณด๋ค ๊ฐ์ ๋ด์ฉ์ด ์ด๋ ค์์ ์ ๋ฆฌํ๋ฉด์ ๋ณต์ตํ๊ณ ๊ฐ์ ๋ด์ฉ์ ์ด์ด์ ๋ค์ด์ผ ํ ๊ฒ ๊ฐ๋ค ๐
์ค๋์ GCD์ ์ข ๋ฅ์ ํน์ฑ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉํ ๋ ์ฃผ์์ฌํญ์ ๋ํด ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋๋ฐ ๊ทธ ์ ์ ์ด์ ์ ๋ค๋ค๋ ๋ด์ฉ์ ํ๋ฒ ์ ๋ฆฌ ํด๋ณด์
GCD(=Dispatch Queue) ๋ ํฌ๊ฒ 3๊ฐ์ง ์ข ๋ฅ๋ก ๋๋ ์ ์์ต๋๋ค.
์ฐ๋ฆฌ๊ฐ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๊ณ ๋ คํ์ง์๊ณ ๋จ์ํ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋ฉ์ธ ์ฐ๋ ๋์์ ๋ชจ๋ ์์ ์ ์ฒ๋ฆฌํ๋ค๊ณ ํ์๋ค. ๊ทธ๋ผ ์ฐ๋ฆฌ๊ฐ ๋ณ๋์ ์ฒ๋ฆฌ๋ฅผ ํ์ง ์์ผ๋ฉด ์์ ๋ค์ด ๋ฉ์ธ ํ์ ํ ๋น๋๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
//DispatchQueue.main.sync {
print("Hello")
//}
์ฝ๋๋ก ํ์ธํด๋ณด๋ฉด ์์ ๊ฐ์ด ์ฐ๋ฆฌ๊ฐ ๋ณ๋์ ์์
์ ํ์ง ์์ผ๋ฉด DispatchQueue.main.sync
๊ฐ ๊ฐ์ธ์ง ํํ๋ก ๋์ํ๋ ๊ฒ์ด๋ค.
(๋ฌผ๋ก ์ค์ ๋ก ์ ๋ ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋ฉ์ธ์ฐ๋ ๋๊ฐ ์ฐ๋ ๋-์ธ์ดํ ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค!)
์ ๋ฆฌํด๋ณด๋ฉด ๋ฉ์ธ ์ฐ๋ ๋๋ ํ๋ ๋ฉ์ธํ๋ ํ๋ ์กด์ฌํ๋ค. ๊ทธ๋ฌ๋๊น ๋ฉ์ธํ๋ ํ์ฐ์ ์ผ๋ก Serialํ ํน์ฑ์ ๊ฐ์ง ์ ๋ฐ์ ์๋ค. ์? ์ฌ๋ฌ ์ฐ๋ ๋์ ๋์ ์์ ์ ๋ฃ์ ์๊ฐ ์์ผ๋๊น!!
๋ฉ์ธ ์ฐ๋ ๋ ์ด์ผ๊ธฐ๊ฐ ๋์จ๊น์ ๋ฉ์ธ ์ฐ๋ ๋์ ์ค์ํ ํน์ฑ์ ๋ํด์ ํ๋ ์ง๊ณ ๋์ด๊ฐ๋ฉด UI ๊ด๋ จ ์์
์ ๋ฉ์ธ ์ฐ๋ ๋์์ ๋์ํ๋ค
์ด๋ iOS๋ฟ๋ง ์๋๋ผ ๋ชจ๋ OS์์ ๋์ผํ๋ค. ์ ๊ทธ๋ด๊น? ๋ง์ฝ ํ๋ฉด์ ํ ๊ด๋ จ ๋ก์ง์ด ์ฌ๋ฌ ์ฐ๋ ๋์์ ์ฒ๋ฆฌ๋์ด์ ์์๊ฐ ๋ณด์ฅ๋์ง ์๊ฒ ๋๋ฉด ์ฐ๋ฆฌ๊ฐ ์๋ํ์ง ์์ ๋์์ด ์ผ์ด๋ ์ ์๋ค!
QoS์๋ 6๊ฐ์ง ์ข ๋ฅ๊ฐ ์๋ค.
let userInteractiveQueue = DispatchQueue.global(qos: .userInteractive)
์ฐ์ ์์๊ฐ ๋์๊ฒ ๋ถํฐ ์ฐจ๋ก๋๋ก ์ ์ผ๋ฉด ์์ ๊ฐ๋ค. ์ฐ๋ฆฌ๊ฐ QoS๋ฅผ ์ค์ ๋ง ํด์ฃผ๋ฉด ์ฐ์ ์์๊ฐ ๋์ ์ผ์ ๋ ๋ง์ ์ฐ๋ ๋๋ฅผ ์์์ ๋ฐฐ์น ํด์ค๋ค.
let customQueue = DispatchQueue(label: "bran")
let customConcurrentQueue = DispatchQueue(label: "bran", attributes: .concurrent)
์ฝ๋๋ก ํ์ธํด๋ณด๋ฉด ์์ ๊ฐ๋ค. QoS๋ฅผ ์ฐ๋ฆฌ๊ฐ ๋ฐ๋ก ์ค์ ํด์ฃผ์ง ์์ผ๋ฉด OS๊ฐ ์์์ ์ถ๋ก ํด์ค๋ค!
์๊น ๋ฉ์ธ ํ์ ๋ํด์ ์ค๋ช ํ ๋ ๋ฉ์ธ ์ฐ๋ ๋์์ UI ๊ด๋ จ๋ ์์ ์ด ์งํ๋๋ค๊ณ ํ์๋๋ฐ ๋ฉ์ธ ์ฐ๋ ๋์์ ์ด๋ค ์์ ์ ํ์ sync ๋ก ๋ณด๋ด๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
๋ง์ฝ Concurrent ํน์ฑ์ ๊ฐ์ง๋ ํ์ task1, task2 ๋ฅผ sync ํ๊ฒ ๋ณด๋๋ค๊ณ ๊ฐ์ ํด๋ณด์.
Concurrentํ ํน์ฑ์ ๊ฐ์ง๋ ํ์ด๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ ์ฐ๋ ๋์์ ์์ ๋ค์ ์ฒ๋ฆฌํ๊ฒ ๋ ๊ฒ์ด๋ค. ํ์ง๋ง syncํ๊ฒ ๋ณด๋๊ธฐ ๋๋ฌธ์ UI๋ฅผ ๋ด๋นํ๊ณ ์๋ ๋ฉ์ธ ์ฐ๋ ๋๋ ๋ค์ ํ์ ๋ณด๋ธ ์์ ๋ค์ด ์๋ฃ๋๊ธฐ ์ ๊น์ง ๋ค์ ์์ ์ ์ด์ด์ ์งํํ ์ ์๋ค!
ํ๋ก ๋ณด๋ธ ์์
๋ค์ด ๋ง์ฝ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ด๋ผ๋ฉด ๊ทธ ์๊ฐ๋์ ์ฑ์ ๋ฉ์ถฐ๋ฒ๋ฆฌ๋ ํ์์ด ๋ฐ์ํ๊ณ ์งง๋๋ผ๋ ์ฐ๋ฆฌ๊ฐ ํผํ๊ณ ์ถ๋ ์ฑ์ด ๋ฒ๋ฒ
์ด๋ ํ์์ด ๋ํ๋๊ฒ ๋๋ค! ๊ทธ๋์ ๋ฉ์ธ ์ฐ๋ ๋์์ ํ์ ์์
์ ๋ณด๋ผ ๋๋ ํญ์ ๋น๋๊ธฐ๋ฅผ ๋ณด๋ด์ผ ํ๋ค!
์ฐ์ ์์ ๊ฐ์ด Task1 ์์ Task2๊ฐ ํฌํจ๋์ด ์๋ ์์ ์ด ์๋ค๊ณ ๊ฐ์ ํด๋ณด์. ํ์ฌ Task1์ Concurrent ํน์ฑ์ ๊ฐ์ง๋ ํ์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ณด๋ธ ์ํ์ด๋ค. ๊ทผ๋ฐ Task1์ ํฌํจ๋ Task2๋ฅผ Concurrent ํน์ฑ์ ๊ฐ์ง๋ ํ์ ๋๊ธฐ์ ์ผ๋ก ๋ณด๋ด๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
DispatchQueue.global().async {
// Task 1
DispatchQueue.global().sync {
// Task2
}
}
์ฝ๋๋ก ํํํ๋ฉด ์ด๋ฐ ์ํฉ์ด๋ค.
Task2๋ฅผ Concurrent ํน์ฑ์ ๊ฐ์ง Queue์ sync ๋ฐฉ์์ผ๋ก ๋ฃ์ด์คฌ๊ธฐ ๋๋ฌธ์ ์ฐ๋ ๋1์ ํด๋น ์์
์ด ์๋ฃ ๋ ๋๊น์ง Block ์ํ๊ฐ ๋๋ค.
Concurrent ํน์ฑ์ ๊ฐ์ง ํ์ด๊ธฐ ๋๋ฌธ์ ํ๋ ๋ค์ด์จ Task2๋ฅผ ์ฐ๋ ๋1, ์ฐ๋ ๋2, ์ฐ๋ ๋3 ์ด๋๋ ๋ฃ์ด ์ค ์ ์๊ณ ์ฐ๋ฆฌ๊ฐ ์ฌ๊ธฐ์ ๊ฐ์
ํ ์๋ ์๋ค.
๊ทธ๋ฐ๋ฐ ์ฐ๋ ๋1์์ Task2๊ฐ ํ ๋น๋๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
Task2๊ฐ ์๋ฃ๋๊ธฐ ์ ์๋ Task1์ ์คํํ ์ ์๋๋ฐ Task2๊ฐ ๋ฉ์ถฐ์๋ ์ฐ๋ ๋์ ํ ๋น๋์๊ธฐ ๋๋ฌธ์ ์๋ฃ๋ ์๊ฐ ์๋ค.
์ด๋ฌํ ์ํฉ์ ๊ต์ฐฉ์ํ(๋ฐ๋๋ฝ)
์ด๋ผ๊ณ ํ๋ค!
๋ฌผ๋ก ํ์์ ๋ค๋ฅธ ์ฐ๋ ๋๋ก Task2๋ฅผ ํ ๋นํ๋ฉด ์์ ๊ฐ์ ์ํฉ์ด ๋ฐ์ํ์ง ์์ง๋ง ๊ทธ๋ด ๊ฐ๋ฅ์ฑ์ด ์กด์ฌํ๋ค. ๋ฐ๋ผ์ ํ์ฌ์ ๊ฐ์ ํ์ sync ๋ก ์์ ์ ๋ณด๋ด๋ฉด ์๋๋ค!
๊ทธ๋ผ ์ด๋ป๊ฒ ์์ ๊ฐ์ ์ํฉ์ ํผํ ์ ์์๊น?
global ํ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, QoS๋ค๋ฅธ ํ๋ฅผ ์์ฑํด์ ์ฌ์ฉํ๋ฉด ์ฐ๋ ๋๊ฐ ๊ฒน์น์ง ์๋๋ค๊ณ ํ๋ค!
์์ ๋์ผํ ์ด์ ๋ก ๋ฉ์ธ ์ฐ๋ ๋์์ DispatchQueue.main.sync ๋ฅผ ์ฌ์ฉํ๋ฉด ์๋๋ค. ์ดํด ๋๊ฒ ์ฃ ?
ํด๋น ๋ด์ฉ์ ์จ๋ฐ๋์ ๊ฐ์๋ฅผ ๋ฃ๊ณ ์ดํดํ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ธ์ ๋๋ค!!
DispatchGroup ๊น์ง ๊ฐ์๋ฅผ ๋ค์๋๋ฐ ์ต๋ํ ๋นจ๋ฆฌ ๋ณต์ตํ๊ณ ์ ๋ฆฌํ๋๋ก ํ๊ฒ ์ต๋๋ค!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.