iOS/๐Ÿค– App

Review of Implement a Custom Control (1) - FoodTracker

woozzang 2021. 1. 21. 22:59

์ด ์žฅ์—์„œ ํ•ด๋ณธ ๊ฒƒ๋“ค

1. Custom source code ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ทธ๊ฒƒ์„ stroyboard ๋‚ด์˜ ์š”์†Œ๊ณผ ์—ฐ๊ฒฐํ•˜๊ธฐ

2. Container view๋กœ์„œ UIStackView ์‚ฌ์šฉํ•˜๊ธฐ

3. ์ฝ”๋“œ๋กœ view ๋“ค์„ ์ƒ์„ฑํ•˜๊ธฐ

4. ์ ‘๊ทผ์„ฑ ์ •๋ณด๋ฅผ custom control ์— ์ถ”๊ฐ€ํ•˜๊ธฐ

5. @IBInspectable, @IBDesignable ์‚ฌ์šฉํ•˜์—ฌ Interface Builder์—์„œ custom view ๋ฅผ ๋„์šฐ๊ณ , ์กฐ์ž‘ํ•˜๊ธฐ


 

Create a Custom View

Container View ์ธ UIStackView ์•ˆ์— 5๊ฐœ์˜ UIButton ์„ ๋„ฃ์–ด์„œ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

 

UIButton ์€ ์„œ๋ธŒํด๋ž˜์‹ฑํ•˜์—ฌ Custom control ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

๋งŒ๋“ค์–ด๋ณผ Custom Control ์˜ ๊ตฌ์„ฑ

์ด contol ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” meal ( ์Œ์‹ ) ์„ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

 

์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๋ณ„์„ tap ํ•˜๋ฉด, ํŠน์ • ๋ณ„์„ ํฌํ•จํ•œ ์ด์ „์˜ ๋ณ„๊นŒ์ง€ ๋ชจ๋‘ ๊ฒ€์€์ƒ‰์œผ๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์žฅ ์˜ค๋ฅธ์ชฝ์˜ ๋ณ„์„ tap ํ•œ๋‹ค๋ฉด, ๋ชจ๋“  ๋ณ„์ด ๊ฒ€์€์ƒ‰์œผ๋กœ ์ฑ„์›Œ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์ฑ„์›Œ์ง„ ๋ณ„ ์ค‘ ๊ฐ€์žฅ ์˜ค๋ฅธ์ชฝ์˜ ๋ณ„์„ ๋‹ค์‹œ tap ํ•˜๋ฉด ๋ชจ๋“  ๋ณ„์ด ๋น„์›Œ์ง‘๋‹ˆ๋‹ค.

 

์šฐ์„  UIStackView๋ฅผ ์„œ๋ธŒํด๋ž˜์‹ฑํ•œ ์ปค์Šคํ…€ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

 


View ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. View๋ฅผ ์ฝ”๋“œ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ

2. Stroyboard ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

 

`1 `์—์„œ๋Š” init(frame:)  ๋ฉ”์„œ๋“œ๋กœ view๋ฅผ ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค.

`2 `์—์„œ๋Š” init?(coder:)  ๋ฉ”์„œ๋“œ๋กœ storyboard์—์„œ view๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

 

Initializer ๋Š” ์‚ฌ์šฉํ•  ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ค€๋น„ํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ๋ผ๋Š” ๊ฒƒ์„ ์ƒ๊ธฐํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ค€๋น„ํ•ด์ค€๋‹ค๋Š” ๊ฒƒ์€ ์ธ์Šคํ„ด์Šค์˜ ๊ฐ ์†์„ฑ์„ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๊ณ  ๋‹ค๋ฅธ setup ๋“ค์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์•ฑ์„ ์„ค๊ณ„ํ•  ๋•Œ, Interface Builder ๋Š”  view ๋ฅผ canvas ์— ์ถ”๊ฐ€ํ•  ๋•Œ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด view๋ฅผ ์ธ์Šคํ„ด์Šคํ™” ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ๋‹จ๊ณ„๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค๋ณด๋ฉด, view์™€ ์—ฐ๊ฒฐ๋œ ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ๋”ฐ๋กœ ์—†์œผ๋ฉด Inspector configuration ์—๋”ฐ๋ผ ์ ์šฉํ•จ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ, ์•ฑ์€ stroyboard ์—์„œ view๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ๋‘ Initialization ๋ชจ๋‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

class RatingControl: UIStackView {
  
  //MARK: Initialization
  override init(frame: CGRect) {
    super.init(frame: frame)
  }
  
  required init(coder: NSCoder) {
    super.init(coder: coder)
  }
}

NOTE

Swift๋Š” ํด๋ž˜์Šค์— ์–ด๋– ํ•œ Initializer๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ, ์ž๋™์œผ๋กœ super class ์˜ ๋ชจ๋“  designated initializer ๋ฅผ ์ƒ์†ํ•ฉ๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ ํ•œ ๊ฐœ ์ด์ƒ์˜ initializer ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด, ๋” ์ด์ƒ super class ์˜ ๋ชจ๋“  designated initializer ๋“ค์€ ์ž๋™์œผ๋กœ ์ œ๊ณต๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ์ด ๊ฒฝ์šฐ super class์— `required` initializer๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ถ”๊ฐ€์ ์œผ๋กœ sub class ๋Š” ๊ตฌํ˜„ํ•œ super class ์˜ `required` initializer ์•ž์— `required` ํ‚ค์›Œ๋“œ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋ถ™์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.



 

Display the Custom View

Story board ์—์„œ Object Library ๋กœ Horizontal Stack View ๋ฅผ  ์”ฌ์— ์ถ”๊ฐ€ ํ•œ ํ›„,

Identity Inspector๋ฅผ ํ†ตํ•ด ์œ„์—์„œ ์ž‘์„ฑํ•œ `RatingControl` Class ์™€ ์—ฐ๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.


 

Add Buttons to the View

 

์ฝ”๋“œ๋กœ ์Šคํƒ ๋ทฐ์— ๋ฒ„ํŠผ์ถ”๊ฐ€ํ•˜๊ธฐ

 

์œ„ ๋‹จ๊ณ„์—์„œ, ์ €๋Š” `RatignControl` ์ด๋ผ๋Š” UIStackView ๋ฅผ ์„œ๋ธŒํด๋ž˜์‹ฑํ•œ Custom class ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ด์ œ ์ด View ์— ๋ฒ„ํŠผ(UIButton) ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์œ„์˜ ๋‘ ๊ฐœ์˜ Initializer ์ค‘ ์–ด๋–ค ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋”๋ผ๋„ ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ private method๋กœ ๋ฒ„ํŠผ์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ›„ ๋‘ ๊ฐœ์˜ Initializer์— ๋ชจ๋‘ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

Here, you are using one of the UIButton class’s convenience initializers. `UIButton()`

This initializer calls init(frame:) and passes in a zero-sized rectangle.

  //MARK: Private Methods
  private func setupButtons() {
    
    // Create the button
    let button = UIButton()
    button.backgroundColor = .red
    
    // Add constraints
    /* 1 */
    button.translatesAutoresizingMaskIntoConstraints = false
    
    /* 2 */
    button.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
    
    /* 3 */
    button.widthAnchor.constraint(equalToConstant: 44.0).isActive = true

    // Add the button to the stack
    /* 4 */
    addArrangedSubview(button)
  }

/* 1 */ ์€ ๋ฒ„ํŠผ์˜ constraints ๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ง‰์Šต๋‹ˆ๋‹ค.

 

view ๋ฅผ ์ฝ”๋“œ์—์„œ ์ธ์Šคํ„ด์Šคํ™” (Instantiate) ํ•  ๋•Œ, translatesAutoresizingMaskIntoConstraints ์˜ ๊ธฐ๋ณธ ๊ฐ’์€ true ์ž…๋‹ˆ๋‹ค.

 

์ด๊ฒƒ์€ layout engine ์ด view ์˜ ์†์„ฑ์ธ frame ๊ณผ autoresizingMask ์— ๊ธฐ๋ฐ˜ํ•ด ์ž๋™์œผ๋กœ view์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ์„ค์ •ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

๋ณดํ†ต AutoLayout ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์ด autogenerated constraints ๋“ค ๋Œ€์‹  ์ง์ ‘ ์„ค์ •ํ•œ ๊ฐ’์„ ์“ฐ๊ณ  ์‹ถ์–ดํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ false ๋กœ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Layout engine์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ constraints = autogenerated constraints

 

/* 2 */ ์™€  /* 3 */ ์—์„œ heightAnchor ์™€ widthAnchor ๋Š” view์˜ ๋†’์ด์™€ ๋„“์ด๋ฅผ ์„ค์ •ํ•˜๋Š” constraints ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

1. Button์˜ heightAnchor ์™€ widthAnchor ์†์„ฑ์€ layout anchor ๋กœ์˜ ์ ‘๊ทผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด layout anchor๋ฅผ ํ†ตํ•˜์—ฌ constraints๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. - ์œ„ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ view ์˜ height ์™€ width๋ฅผ ๊ฐ๊ฐ ์ •์˜ํ•˜๋Š” constraints ๋ฅผ ์ƒ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

2. Anchor์˜ constraint(equalToConstant:) ๋ฉ”์„œ๋“œ๋Š” immutable ํ•œ height ํ˜น์€ width ํฌ๊ธฐ๋ฅผ ๋ถ€์—ฌํ•˜๋Š” constraint๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

3. Constraints ์˜ isActive ์†์„ฑ์„ ํ†ตํ•ด ํ™œ์„ฑ/๋น„ํ™œ์„ฑ์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ’์ด true๋ผ๋ฉด, ์‹œ์Šคํ…œ์€ ๋งž๋Š” view ์— constraints ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ํ™œ์„ฑํ™” ํ•ฉ๋‹ˆ๋‹ค.

 

๊ฒฐ๊ณผ์ ์œผ๋กœ /* 1 */ /* 2 */ /* 3 */ ์„ ํ†ตํ•ด Button ์„ ๊ณ ์ •๋œ ํฌ๊ธฐ์˜ object๋กœ ์ •์˜ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ( 44 points x 44 points )

 

/* 4 */ ์—์„œ addArrangedSubview( )  ๋ฉ”์„œ๋“œ๋Š” RatingControl ์Šคํƒ๋ทฐ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” view์˜ ๋ฆฌ์ŠคํŠธ์— button ์„ ๋„ฃ๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด button์€ RatingControl์˜ subview๊ฐ€ ๋˜๊ณ , RatingControl ์—๊ฒŒ RatingControl ๋‚ด๋ถ€์—์„œ button ์˜ ์œ„์น˜๋ฅผ ๊ด€๋ฆฌํ•  contstraints๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ์•Œ๋ ค์ค๋‹ˆ๋‹ค.


 

์ฝ”๋“œ๋กœ ๋ฒ„ํŠผ์— ์•ก์…˜ ์ถ”๊ฐ€ํ•˜๊ธฐ

private func setUpButton() {
  ...

  button.addTarget(self, action: #selector(self.ratingButtonTapped(button:)), for: .touchUpInside)
  
  addArrangedSubView(button)
}

@objc func ratingButtonTapped(button: UIButton) {
  //code
}

 

์œ„ ์ฝ”๋“œ๋Š” target-action pattern ์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

 

addTarget(_, action:, for) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. button ์—์„œ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด button์— touchUPInside ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ• ๋•Œ๋งˆ๋‹ค ratingButtonTapped(button:) ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

 

addTarget(_, action:, for) ๋ฉ”์„œ๋“œ๋ฅผ ์ž์„ธํ•˜๊ฒŒ ๋ถ„์„ ํ•ด๋ณด๋ฉด:

 

์ฒซ๋ฒˆ์งธ parameter ์—๋Š” target ์ด ๋“ค์–ด๊ฐ€๋Š”๋ฐ, action์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์— ์ „๋‹ฌํ•ด์•ผํ•˜๋ฏ€๋กœ `self ๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋‘๋ฒˆ์งธ parameter ์—์„œ #selector ๋Š” ์ฃผ์–ด์ง„ ๋ฉ”์„œ๋“œ์˜ Selector ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

Selector๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๋ถˆํˆฌ๋ช…ํ•œ ๊ฐ’ ์ž…๋‹ˆ๋‹ค.

 

์˜ค๋ž˜๋œ API๋“ค์€ ๋ฉ”์„œ๋“œ๋“ค์„ ๋™์ ์œผ๋กœ ๋ถ€๋ฅผ๋•Œ selector๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

performSelector(_:) and addTarget(_:action:forControlEvents:) ๊ฐ€ ๊ทธ ์˜ˆ๊ฐ€ ๋˜๊ณ  ์•„์ง ์‚ฌ์šฉํ•˜๋Š” API์ž…๋‹ˆ๋‹ค.

 

#selector(RatingControl.ratingButtonTapped(_:)) ๊ฐ€ ratingButtonTapped(_:) action method ์˜ selector ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด ์ค๋‹ˆ๋‹ค.

 

์„ธ๋ฒˆ์งธ parameter ์—๋Š” UIControlEvents ํƒ€์ž…์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ์ด ํƒ€์ž…์€ control ์ด ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋ฅผ ์ •์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

์œ„ ์ฝ”๋“œ์—์„œ๋Š” .touchUpInside ์ด๋ฒคํŠธ๋ฅผ parameter๋กœ ์ „๋‹ฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ, Interface Builder ๋กœ ์•ก์…˜์„ ์—ฐ๊ฒฐํ•˜๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, IBAction attribute ๋ฅผ action ๋ฉ”์„œ๋“œ ์•ž์— ์ •์˜ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ์˜ parameter ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

func doSomething()

/* ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ control ์ธ์Šคํ„ด์Šค ์ „๋‹ฌ */
func doSomething(sender: UIButton)

/* control ์ธ์Šคํ„ด์Šค plus ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ ์ข…๋ฅ˜ ์ „๋‹ฌ */
func doSomething(sender: UIButton, forEvent event: UIEvent)

N O T E

 

๊ด€๋ จ ๊ธ€: woozzang.tistory.com/50

 

์ถ”๊ฐ€์ ์œผ๋กœ `Touch Up Inside` ๋Š” ์ด๋ฒคํŠธ์˜ ์ข…๋ฅ˜๋กœ์„œ, ์ผ๋ฐ˜์ ์œผ๋กœ ์ƒ๊ฐํ•˜๋Š” ํƒญ ๋™์ž‘์— ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์ž…๋‹ˆ๋‹ค.

 

์—„๋ฐ€ํžˆ ์ด์•ผ๊ธฐํ•˜๊ธฐ ์œ„ํ•ด `Touch Up Outside` ์ด๋ฒคํŠธ์™€ ๋น„๊ตํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์‚ฌ์šฉ์ž์˜ ๋™์ž‘์„ ์ผ๋‹จ Touch์™€ Up์œผ๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์ƒ๊ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

๋ง ๊ทธ๋Œ€๋กœ 1. (์†๊ฐ€๋ฝ์œผ๋กœ) ํ„ฐ์น˜ํ•˜๋Š” ๋™์ž‘๊ณผ 2. (์†๊ฐ€๋ฝ์„) ๋–ผ๋Š” ๋™์ž‘์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

Touch Up Inside ๋Š” ๋ทฐ๋ฅผ ํ„ฐ์น˜ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ์—์„œ ํ•ด๋‹น ๋ทฐ์˜ ๋ฐ”์šด๋”๋ฆฌ ๋‚ด(Inside)์—์„œ ์†๊ฐ€๋ฝ์„ ๋–ผ๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

 

์ผ๋ฐ˜์ ์ธ ํƒญ ๋™์ž‘์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Touch Up Outside ๋Š” ๋ทฐ๋ฅผ ํ„ฐ์น˜ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ์—์„œ ๋“œ๋ž˜๊ทธํ•œ ์ฑ„๋กœ ์ด๋™ํ•˜์—ฌ ๋ทฐ์˜ ๋ฐ”์šด๋”๋ฆฌ ๋ฐ”๊นฅ(Outside)์—์„œ ์†๊ฐ€๋ฝ์„ ๋–ผ๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.


 

 

 

๋!