이 포스트는 다음의 글을 번역한 글입니다.


원글 :

iOSエンジニア必見!!iOSのレイアウトで押さえておきたいこと【総集編】

https://developers.eure.jp/tech/ios_layout_beginners/


안녕하세요! Couples의 iOS엔지니어 단(丹)입니다.

이번에는 iOS 개발자라면 반드시 집고 넘어가야할 View의 레이아웃에 대해서 정리해보겠습니다.

View의 레이아웃은 어플을 만드는데 있어 기본중의 기본이지만 깊은 이해가 없더라도 어플이 동작하게 만들수는 있습니다. 그러나, 퍼포먼스를 의식해야 하거나 설계를 제대로 하는데 있어서 View의 레이아웃을 이해하는 것은 필수 입니다. 레이아웃을 더 깊이 이해하는 것을 돕기 위해 이 글이 조금이나마 참고가 될수 있다면 좋겠습니다. 대상 독자는 초급자부터 중급자 대상입니다.


이 포스트에서는 Xcode 7.2.1, Swift 2.1을 사용하였습니다.


목차

  • View와 ViewController의 레이아웃 사이클
  • Constraints
    • View의 updateConstraints
    • View의 Intrinsic Content Size 란?
    • content Hugging과 Compression resistance
    • Intrinsic Content Size를 동적으로 변경하기
    • ViewController의 updateViewConstraints 
    • 커스텀 뷰는 requiresConstraintBasedLayout를 사용하자
  • Layout
    • ViewのlayoutSubviews
    • NSLayoutConstraint의 값을 갱신한 경우
    • ViewControllerのviewWillLayoutSubviews와 viewDidLayoutSubviews
  • View의 레이아웃 사이클 메소드 정리
  • AutoLayout과 애니메이션
  • 코드로 AutoLayout작성
    • 코드로 AutoLayout을 작성할시 주의 점
    • Couples에서 사용하고 있는 AutoLayout 라이브러리 「Cartography」에 대하여 
  • 끝으로


View와 ViewController의 레이아웃 사이클 

View와 ViewController의 레이아웃 사이클은 반드시 집고 넘어가야 합니다. 어플의 백그라운드에서 어떻게 움직이고 있는지 이해하기 위해 레이아웃 사이클을 이해하는 것은 엄청나게 중요합니다. 이번에는 Subview가 하나도 없는 비어있는 ViewController를 준비합니다. 이 ViewController를 표시 했을 때 아래의 순서대로 ViewController와 ViewController가 갖고 있는 view의 각 메소드가 호출 됩니다. 


レイアウトサイクル


설명이 필요한 메소드는 별도로 설명하겠습니다. 여기에서 대략적으로 3개의 스텝이 있다는 것을 기억해주십시오.


  • step 1. 제약(Constraints)
    이 스텝에서 Autolayout의 제약(Constraints)을 갱신합니다. 제약(Constraints)의 갱신은 subview로부터 superview의 순서로 호출 됩니다.

  • step2. 레이아웃(layout)
    step1의 제약(Constraints)을 바탕으로 레이아웃을 실행합니다. 여기에서 view의 center와 bounds를 결정합니다. 레이아웃의 갱신은 superview로부터 subview의 순서로 호출됩니다.

  • step 3. 그리기(Draw)
     step2의 레이아웃후에 UIView의 drawRect(rect:CGRect)가 호출 됩니다. 이 그리기 스텝에서는 CoreGraphics를 사용하여 그리게 됩니다. Core Graphics에서 그리기가 필요한 어플은 그다지 많지 않다고 생각됨으로 본 포스트에서는 설명을 생략하고 있지만 반드시 학습해 보시길 권유합니다.

위의 내용을 정리하자면, 레이아웃 사이클은 1. 제약(Constraints), 2. 레이아웃, 3.그리기 의 순서로 스텝 1,2는 UIView와 UIViewController 양쪽에 메소드가 내장되어 있습니다.


ConstraintsとLayoutの更新順序



Constraints

여기서 부터는 각각의 사이클에 대해 상세하게 설명해보고자 합니다. 우선은 제약(Constraints) 부분을 보도록 하겠습니다.

View의 updateConstraints

updateConstraints의 사용법

UIView의 updateConstrains는 제약(Constraints)을 갱신할때 호출되는 메소드 입니다. 서브클라스에서 오버라이드하여 사용합니다. 아래와 같이 적습니다. super를 가장 아래 호출하고 있는 것에 주의 해주십시오.


1
2
3
4
5
6
7
8
override func updateConstraints() {
    // 제약을 갱신하는 코드를 여기에 적습니다. 
    // 아직 레이아웃을 실행하는 setNeedsLayout, layoutIfNeeded는 호출하면 안됩니다. 
    // setNeedsDisplay도 또한 호출하면 안됩니다. 
 
    // super는 가장 마지막에 호출합니다. 
    super.updateConstraints()
}
cs


updateConstraints의 호출방법

어플 내에서 콘텐츠가 변경된 경우, view의 제약(Constraints)을 갱신하고 싶을 때가 있을 겁니다. 이때, View의 updateConstrains를 코드로 직접 호출하면 안됩니다. 이것은 시스템이 적당한 타이밍에 호출해줍니다. 시스템에 제약(Constraints)의 갱신을 요청하기 위해 UIView에 setNeedsUpdateConstrains라는 메소드가 내장되어 있습니다.


setNeedsUpdateConstraints를 호출하는 순간은 제약(Constraints)의 갱신을 하지 않고 시스템이 한번에 정리하여 제약(Constraints)의 갱신을 실행하게 됩니다. 즉, 이라와 같은 setNeedsUpdateConstrains를 반복하여 호출해도 제약(Constraints)이 갱신되는것은 한번 뿐입니다. 이 메소드는 두의 updateConstraints를 호출하기 위해 표시해두는 의미로 사용합니다.


1
2
3
4
5
6
func doSomething() {
    for _ in 0 ..< 10 {
        // 반복하여 호출하여도 updateConstraints는 한번만 호출됩니다.
        view.setNeedsUpdateConstraints()
    }
cs

 


 또한 시스템이 정리해서 제약(Constraints)의 변경을 실행할 때 updateConstraintsIfNeeded를 호출합니다. 이 메소드는 개발자가 호출하는 것도 가능합니다. 이 메소드를 호출하기 전 setNeedsUpdateConstrains를 호출한 View만 updateConstrains가 호출 됩니다. 아래의 그림과 같이 view1과 3에서 setNeedsUpdateConstrains를 호출하여, view2에서 updateConstrainsIfNeeded를 호출한 경우 직후에 updateConstrains가 호출되는 것은 view3뿐입니다. view1은 view2의 Superview이므로 호출되지 안흣ㅂ니다. 이 경우 시스템이 view1의 updateConstraints를 호출합니다.


View의 Intrinsic Content Size란?

View는 Intrinsic Contents Size라고하는 독자의 사이즈를 갖고 있습니다. 사이즈 이므로 Horizontal과 Vertical 양방향으로 설정되어 있습니다. 한반향만 설정하는 것도 가능합니다. UILabel의 경우에는 텍스트를 딱 감쌀수 있는 사이즈가 Intrinsic Content Size 입니다. 또한 UIProgressView에서는 Vertical방향만 설정되어 있습니다. UIView에는 양축 모두 설정되어 있지 않습니다.


Intrinsic Content Sizeが定義されている方向の例


커스텀 뷰에서는 아래와 같이 오버라이드하여 설정가능합니다. 설정하지 않은 방향에서는 UIViewNoIntrinsicMetric을 설정합니다.


1
2
3
override func intrinsicContentSize() -> CGSize {
    return CGSize(width: UIViewNoIntrinsicMetric, height: 10)
}
cs


Intrinsic Content Size를 설정하는 의미를 다음에 설명하겠습니다.


Content Hugging과 Compression resistance

Intrinsic Content Size가 정의 되어 있는 경우에는 Width와 Height를 결정하는 제약(Constraints)을 추가하지 않는 경우 Intrinsic Content Size의 사이즈에 리사이즈 됩니다. 이러한 이유는 Intrinsic Content Size가 설정되어 있는 경우에 디폴트로 아래의 제약(Constraints)이 추가되기 때문 입니다.


// UILabel의 Intrinsic Content Size = CGSize(width: 100, height: 30)의 경우 

H:[label(<=100@250)]

H:[label(>=100@750)]]

V:[label(<=30@250)]

V:[label(>=30@750)]


Width와 Height를 경정하는 제약(Constraints)을 추가하지 않은 경우 위의 4개의 제약(Constraints)에 따라 라벨 사이즈는 w:100, h:30이 됩니다. 우의 표기는 제약(Constraints)의 Visual Format입니다. Visual Format은 다음과 같이 읽습니다.


Horizontal방향에 label의 폭이 100이하의 제약(Constraints). 우선순위(Priority)는 250

H:[label(<=100@250)]

 

Vertica방향에 label의 높이가 30이상의 제약(Constraints)우선순위(Priority)는 750

V:[label(>=30@750)]



자세한 것은 Apple 문서에 적혀 있습니다.

https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html


위의 제약(Constraints)은 각각 이름이 붙어 있습니다.


H:[label(<=100@250)] <- Content Hugging

H:[label(>=100@750)] <- Compression Resistance


Content Hugging이 커지기 어려운 정도, compression resistance가 작아지기 어려운 정도를 나타냅니다. 각각의 프라이오리티가 다른 정도로 기타 제약(Constraints)에 따라 커질지 작아질지가 결정됩니다.


인터페이스 빌더(Interface Builder)나 코드에 설정하는 제약(Constraints)의 디폴트의 우선순위(priority)는 1000입니다. 그것 때문에 기본적으로 개발자 지신이 설정한 제약(Constraints)이 우선되리라 생각됩니다. Content Hugging과 Compression Resistance의 사용처는 UILabel이나 UIButton등 콘텐츠에 따라 동적으로 사이즈가 변할 때, 다시한번 넓이와 높이의 제약(Constraints)을 새롭게 결정하고 싶지 않을 때 사용합니다.


Content Hugging과 Compression Resistance는 InterfaceBuilder와 코드로 우선순위(priority)를 변경하는 것이 가능합니다.


view.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: UILayoutConstraintAxis.Horizontal)


Interface BuilderでContent HuggingとCompression Resistanceを変更する方法



Intrinsic Content Size를 동적으로 변경하기

Intrinsic Content Size를 참조하는 것은 View가 updateConstraints를 호출한 직후입니다. 그러나, 자동적으로 참조하는것은 처음 한번 뿐입니다. 거기서 커스텀뷰에서는 invalidateIntrinsicContentSize를 호출할 필요가 있습니다. 예를들어 UILabel은 text가 변경될 때 invalidateIntrinsicContentSize를 호출, Intrinsic Content Size를 재계산합니다.


커스텀뷰는 requiresConstraintBasedLayout를 사용

모든 제약(Constraints)을 updateConstraints안에 기술하고 있는 경우, 시스템은 updateConstraints를 호출하지 않습니다. AutoLayout에서 동작하는 커스텀뷰를 만들 때에는 UIView의 class func requiresConstraintBasedLayout() -> Bool을 오버라이드하는 것으로 해결합니다.


1
2
3
override class func requiresConstraintBasedLayout() -> Bool {
    return true
}
cs


ViewControllerのupdateViewConstraints

 UIViewController의 updateViewConstraints는 아래와 같이 적습니다.


1
2
3
4
5
6
override updateViewConstraints() {
    super.updateViewConstraints()
    //여기에 self.view의 Subview의 제약을 갱신하는 코드를 적습니다. 
}
 
 
cs

ViewController의 updateViewConstraints는 ViewController의 self.view의 updateConstraints의 대신입니다.


1
2
3
4
5
6
7
class CustomViewController: UIViewController {
    func doSomthing() {
        self.view.setNeedsUpdateConstraints()
        self.view.updateConstraintsIfNeeded()
        // 여기서 self.updateViewConstraints()가 호출됩니다. 
    }
}
cs




Layout

다음에 레이아웃 스텝입니다. 이 레이아웃 스텝시에는 제약이 확정되어 있고 그 제약(Constraints)을 바탕으로 뷰의 레이아웃을 실행합니다.


뷰의 layoutSubviews

layoutSubviews의 사용법

오버라이드시에는 super.layoutSubviews()를 반드시 호출합니다.


1
2
3
4
override func layoutSubviews() {
    super.layoutSubviews()
    // 여기에 코드
}
cs

주의 점으로는 이 메소드는 시스템을 호출하는 것으로 직접 호출하면 안됩니다. 이러한 개념은 제약(Constraints)과 같습니다.


layoutSubview의 호출 방법

이것도 setNeedsUpdateConstrains, updateConstrainsIfNeeded와 개념은 완전하게 같습니다. layoutIfNeeded를 호출한 View와 그 Subview중, setNeedsLayout을 호출한 View에 대하여 layoutSubview를 호출합니다.


NSLayoutConstraint의 값을 갱신한 경우

NSLayoutConstraint의 값을 갱신한 경우, 레이아웃은 자동적으로 조정되는 걸까요? 간단한 예로 검증해 봅시다. 그림과 같이 view1과 view2의 마진의 제약(Constraints)을 leftConstraint라고 합시다.


NSLayoutConstraintの値を更新した場合の例


이 leftConstraint의 값을 변경한 경우, 제약(Constraints)이 관계하고 있는 View 중 부모의 View의 setNeedsLayout이 호출했을 때와 비슷한 움직임입니다. 상세한 것은 아래를 참고해 주세요.


1
2
3
4
5
6
7
@IBOutlet weak var leftConstraint: NSLayoutConstraint!
 
func doSomething() {
    leftConstraint.constant = 100 // 제약을 변경합니다.
    view2.layoutIfNeeded() // 아무것도 일어나지 않습니다. 일반적으로 view2의 layoutSubviews가 호출되어야 합니다. 
    view1.layoutIfNeeded() // view1, view2의 layoutSubviews가 호출 됩니다 .
}
cs


즉, View1의 setNeedLayout이나 layoutIfNeeded를 적지 않아도 제약(Constraints)대로 레이아웃 됩니다.





ViewController의 viewWillLayoutSubviews와 viewDidLayoutSubviews

 ViewController에 있는 viewWillLayoutSubviews, viewDidLayoutSubview. 이 두가지의 메소드는 self.view의 layoutSubviews의 직후에 호출됩니다. 샘플 소스는 아래와 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
class CustomViewController: UIViewController {
    @IBOutlet weak var label: UILabel! // self.viewのSubviewです
 
    func doSomthing() {
        print("1")
        self.label.setNeedsLayout()
        self.view.setNeedsLayout()
        print("2")
        self.view.layoutIfNeeded()
        print("3")
    }
}
cs


1
2
3
4
5
6
7
8
- Console -
1
2
ViewController: viewWillLayoutSubviews
ViewController's view: layoutSubviews
ViewController: viewDidLayoutSubviews
label: layoutSubviews
3
cs



ViewController.view.Subview.layoutSubviews은 viewDidLayoutSubviews의 후에 호출되는 것에 주의 하십시오. 레이아웃은 Superview로부터 subview로 진행됩니다. 





View의 레이아웃 사이클 메소드 정리

위에서 설명했던 View의 레이아웃 사이클의 메소드를 정리하겠습니다.

알기 쉽게 아래와 같이 명명했습니다.(정식 명칭이 아닙니다)


업데이트 메소드 : 기 메소드에서 값을 갱신합니다.

마크메소드 : 업데이트 메소드를 호출해야하는 View에 마크를 표시합니다.

트리거메소드 : 마크가 되어 잇는 View에 대해 업데이트 메소드를 각각호출 합니다.


사이클

업데이트

마크트리거
ConstraintsupdateConstraintssetNeedsUpdateConstraintsupdateConstraintsIfNeeded
LayoutlayoutSubviewssetNeedsLayoutlayoutIfNeeded
DrawdrawRectsetNeedsDisplay, setNeedsDisplayInRect없음


본 포스트에서는 설명하지 않고 있지만 그리기 사이클에서는 setNeedDisplay와 setNeedsDisplayInRect를 다시그리기의 마크를 붙이기 위해 사용하였습니다.





AutoLayout과 애니메이션

AutoLayout에서 애니메이션을 사용하는 경우, 프레임을 조작하는 통상의 애니메이션과는 다른 방식으로 코딩을 합니다.

아래에 비교 샘플을 적어봤습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// frame을 변경하는 방법 
UIView.animateWithDuration(1.0, animations: { () -> Void in
    view.frame.size.width = 100
})
 
// 제약을 변경하는 방법
// Constraint가 정의되어 추가 되있는 것으로 합니다.
let widthConstraint: NSLayoutConstraint = ...
view.addConstraint(widthConstraint)
 
// 애니메이션전에 Constraint를 갱신
widthConstraint.constant = 100
 
// 레이아웃 실행
UIView.animateWithDuration(1.0, animations: { () -> Void in 
    view.layoutIfNeeded()
})
cs


AutoLayout에서 애니메이션을 실행하는 경우 먼저 제약(Constraints)을 갱신해 둡니다.

레이아웃 사이클의 3스텝을 생각해보면 알기쉬울거라 생각합니다. 레이아웃전에 스텝1의 제약(Constraints)이 완성되어 있을 필요가 있으므로, 애니메이션 블록의 전에 제약(Constraints)을 갱신하고 있습니다. 그리고 애니메이션의 구현부분은 프레임이 변경되어 있으므로 스텝2의 레이아웃에 해당합니다. 이미 레이아웃을 실행하기 원하므로 layoutIfNeeded를 사용합니다. 여기서 모든 기본은 제약(Constraints), 레이아웃, 그리기의 3스텝입니다.





코드로 AutoLayout을 구현

코드로 AutoLayout을 구현시 주의 점

코드로 AutoLayout을 구현할 경우, interfaceBuilder와는 다르게 UIView의 var translateAutoresizingMaskIntoConstraints:Bool을 false로 해 두어야 합니다. 이 프로퍼티는 디폴트로 true이며, true의 경우 View의 Autoresizing mask의 값으로부터 제약(Constraints)이 자동적으로 추가됩니다. 이 제약(Constraints)이 개발자 자신이 추가한 제약(Constraints)과 충돌을 일으켜 레이아웃이 이상해 질 경우도 있습니다.

Interface Builder에서 AutoLayout을 만들 때에는 시스템이 자동으로false로 설정되어 있으므로 코드에서 AutoLayout을 구현할 때에만 주의하여 주십시오.





Couples에서 사용하고 있는 Autolayout의 라이브러리 Cartography에 대하여

Couple에서는 현재 코드로 Autolayout을 구현하고 있습니다. 이 때 UIKit의 표준 API로는 제약(Constraints)을 하나 추가하는것만으로 엄청난 코드를 작성해야 합니다. 이것을 해소하기 위해 Cartography라는 라이브러리를 도입하고 있습니다. 이위에도 SnapKit또한 검토했지만 Cartography가 제일 Swift를 잘 활용하고 있어 채용하였습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 표준 API
addConstraint(NSLayoutConstraint(
    item: button1,
    attribute: .Right,
    relatedBy: .Equal,
    toItem: button2,
    attribute: .Left,
    multiplier: 1.0,
    constant: -12.0
))
 
//CartoGraphy
constrain(button1, button2) { button1, button2 in
    button1.right == button2.left - 12
}
cs

게다가 위의 예에서는 좌측의 Button1의 translateAutoresizingMaskIntoConstraints를 false로 만들어 줍니다. 이 외에도 편리한 메소드가 내장되어 있으므로 상세는 Readme를 읽어주십시오.





끝으로

이번에는 iOS의 레이아웃 사이클을 중심으로 View의 레이아웃이 실행되는 방법을 정리해 보았습니다. 레이아웃에 관한 메소드는 많이 존재하므로 모든 것을 파악하기는 힘들다고 생각됩니다. 저도 처음에는 고생했어요.


iOS초심자분들은 이번에 소개한 제약(Constraints), 레이아웃, 그리기의 3스텝의 흐름과 setNeeds...등의 메소드의 사용방법을 이해해두시면 좋을 것이라 생각됩니다. 그 후에는 UIKit의 리퍼런스를 읽고 프로퍼티나 메소드를 사용하는 방법을 늘려간다면 점차 적응되리라 생각도비니다.

(참고로, 리퍼런스를 읽을 때에는 Dash라고하는 Mac의 어플리케이션이 큰 도움이 됩니다.)


이상 읽어주셔서 감사합니다.


Constraints 제약, 제한, 통제

Intrinsic 고유한, 본질적인

Hugging 껴안다, 바짝 붙어있다.

Compression 압축

resistance 저항

Calendar caldr = Calendar.getInstance();

int limitYear=caldr.get(Calendar.Year);

DatePickerDialog dpdTemp = new DatePickerDialog(getContext(),new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

}
}, GTODAY.YYYY(), GTODAY.MM(), GTODAY.DD());

caldr.set(limitYear, 0,1);
dpdTemp.getDatePicker().setMinDate(caldr.getTimeInMillis());
caldr.set(limitYear, 11, 31);
dpdTemp.getDatePicker().setMaxDate(caldr.getTimeInMillis());
dpdTemp.show();




안드로이드 4.0 이상에서 이것저것 만져보다가

그냥 이걸로 결정.

어차피, 일, 월만 결정하는건데 어렵게 조정 안하고, 원하는 날자 내에서만 선택 할 수 있게 조정했다.


'Android' 카테고리의 다른 글

[Android] 여는글... 이랄까?  (0) 2011.06.07

android studio

eclipse content assist


== class name completion

이번 프로젝트를 루비로 진행하기로 마음을 먹고

우분투 14.02(?)버전에 루비를 설치하기로 한다.


하루종일걸려 어떻게 설치를 했지만

mySQL데이터 베이스와 연동이 되지 않아 좌절..OTL...


그리고 오늘 다시 설치를 시작했다.


그런데 분명히 한번 웹서버까지 돌렸다가 콘솔창만 껐다 켜면 안돌아가는 기이한 형상에 맞딱드렸다..

아직 왜 인지는 모르겠으나, 에러 내용은


rails 명령어를 찾을 수 없다는 것이었고

그래서

gem install rails로 설치를 하려고 하자

zlib을 찾을 수 없다고 나온다.


그래서

다시 처음부터 한다.

rvm uninstall all --force

rvm cleanup all

rvm pkg install zlib

\curl -sSL https://get.rvm.io | bash -s stable --ruby

source /home//gadmin/.rvm/scripts/rvm

gem install rails

bundle install

rails server


를 했더니 잘된다.


근데 또 써버를 죽이고 터미널 종류후 다시 열었더니 다시 rails와 zlib을 못찾는다 징징댄다.


그럴 땐 다른거 하지 말고 그냥.

source /home/gadmin/.rvm/scripts/rvm

이 명령어 하나만 적어주면 잘 돌아간다.


아... 루비 코딩은 언제 시작할 수 있으려나...


무식하게 소수 판별하는 방법


__int64 isPrime(__int64 n)

{

for(__int64 i=2;i<n;i++)

if(n%i==0)

return 0;

return 1;

}


__int64로하는 것은 큰 숫자 판별하기 위해서...

근데 사실 i7으로 돌려도 10개이상은 몇십분이상 걸린다...

그리고 그 이상으로 숫자가 커지면 기하급수적으로 시간이 걸림...

왜 암호화에서 소수를 사용하는지 알것만도 같다.

'Programming > C/C++' 카테고리의 다른 글

C++에서 복사생성자를 사용하는 이유.  (0) 2011.03.16

 최근 연구실에서 띵가 띵가 거리고 있던 도중, 교수님이 갑작스레 "자네, 학회에서 발표 한번 해보지 않겠나?" 라고 하셔서, 발표 논문을 보고 있다. 내가 쓴논문도 아니고, 이 연구실에서 영어로 쓴 논문이었는데, 내가 거의 관심을 두지 않고 있던, 네트워크 쪽이다. 완전 관심이 없는 건 아니고, 공부를 거의 해본적이 없는 분야이기 때문에 사전지식이 조금 부족한 관계로 이것저것 공부하면서 열심히 파던 도중. 한국어로 제대로 된 자료가 없어서 이렇게 포스팅을 한다. 우선 소개할 내용은 애드훅(Ad Hoc) 무선 네트워크상에서 사용하는 DART(Dynamic Address RouTing) 프로토콜에 대해서 소개를 하겠다.



 우선 이 DART 프로토콜은 캘리포니아 대학의 Jakob Eriksson, Michalis Faloutsos, Srikanth Krishnamurty에 의해 개발 되었다.

 이것을 개발하게 된 배경은 애드훅 네트워크는 어느정도 크게 만들수 있을까? 라는 물음에서 시작되었다. 현재의 애드훅 라우팅 구조는 100개 이상의 노드가 있을때 크기를 더 늘리거나 효율적으로 작동할 수 없다. 이러한 라우팅 프로토콜들은 모바일(무선) 환경에서 노드가 증가함에 따라 그 오버헤드가 기하급수적으로 늘어나는 정적 Addressing을 사용한다. 이 DART의 주된 개념은 노드의 주소와 아이덴티티를 분리하는 것이다. 따라서, DART는 아래의 특성들을 만족시킨다.

 - 오버헤드의 지역화

 - 경량, 분산 프로토콜

 - 제로-설정

 - 하드웨어 제약을 최소화



원리. 

 - 아이덴티티와 주소.

 피어의 아이덴티티는 전역적으로 유일하고 노드의 라이프타임과 동일한 시간동안 유지되는 정적인 숫자이다. 노드의 주소는 k-bit 숫자로 표현된다. 이것은 노드의 움직임에 따라 동적으로 변화한다. 더욱이, 주소는 이것의 현재 값이 노드의 위치에 반영되기 때문에 topological한 의미를 갖는다. "주소-상이-아이덴티티" 페러다임은 전통적인 네트워크 프로토콜인 IP와는 전혀 다르다.


 - 주소 공간

  주소 트리는 주소공간의 관점으로부터의 추상 시각화를 한 것이다. 이것의 잎들은 그들의 주소들에 의해 네트워크 주소안에 있는 피어들을 표현한다. 이것의 안에 있는 노드들은 주소 서브트리를 표현한다. 주소 서브트리는 같은 앞자리 고정 주소 를 갖고 있는 노드들로 구성된다. 점선으로 되어 있는 것은 노드들에 관계된 것들 사이의 물리적인 링크(무선 혹은 유선)들을 가르킨다. 같은 서브트리의 노드들은 물리적으로 연결되어 있다. 서브트리의 구분자는 서브 트리로부터의 주소를 갖고있는 모든 노드의 최소한의 구분자(identifier)이다. 점선으로 된 잎들은 현재 사용되지 않고 있는 주소들이다.


 네트워크 토폴로지의 관점은 노드들 사이의 연결성을 표현한다. 중대한 제약(Prefix Subgraph Constraint)은 네트워크 토폴로지에서 연결된 서브그래프로부터 주어진 주소 앞자리 고정자(address prefix)를 공유하는 모든 노드이다. 주소 트리에서 서브 그래프의 앞자리 고정 주소가 길어질수록, 이 앞자리 고정자(prefix)를 공유하는 노드들 사이의 예상 거리는 줄어든다.


 주소공간에서 또 다른 중요한 추상적 관점은 형제(Sibling)를 보는 관점이다. I-bit로 구성되 있는 서브 트리는 2 레벨-(I-1)-서브트리와 4 레벨-(I-2)-서브트리 그리고 몇몇개의 서브트리들로 구성된다. 잎들은 레벨-0-서브트리들이다. 각각의 I-bit 주소는 I 레벨-k-형제들을 갖고 있다. 



라우팅

 여기에서는 선조치 거리 백터 라우팅 형식이 사용되지만, 그러나 다른 라우팅 방법들(예를들어 link-state Routing) 또한 동적 주소할당에 적합할수 있다. 노드의 라우팅 테이블은 그 노드의 각각의 레벨-K-형제의 엔트리로 구성된다. 만약 노드가 자신의 주소와 같은 앞자리 고정자를 공유하고 있는 피어의 주소에 패킷을 보내고 싶다면, 패킷은 레벨-((주소길이-1)-Prefix길이)-형제의 대표에게 패킷을 보낸다. 예를들어 노드 100이 노드 101에게 패킷을 보내고 싶다. 이것은 그 피어와 2-bit 주소 앞자리 고정자를 공유하기 때문에, 노드 100은 패킷을 그의 Level-0형제의 대표에게 보낸다(이 경우 노드 101 자신). The Prefix Subgraph Constraint는 같은 주소 앞자리 고정자를 공유하기 때문에 레벨-k-형제의 대표가 목적 피어와 연결되는 것을 보증한다. 절차는 패킷이 이것의 목적지까지 도달할때 까지 노드를 받는것으로 계속해서 반복된다.

 균형잡힌 주소 트리를 가정해 보면, 평균적인 라우팅 테이블은 O(log n)엔트리들(n은 노드의 수)로 구성된다. 피어는 이것의 주기적인 업데이트에 의해 이것의 이웃들에게 이것의 라우팅 엔트리를 전파한다.


노드 찾기.

 중요한 질문은 어떻게 노드의 현재의 주소를 찾느냐 이다. 하나의 약속된 접근은 모든 노드들이 <구분자(identifier),주소>쌍의 공유된 분산 노드 찾기표를 사용하는것이다. 노드 Y의 주소를 저장하는 노드는 Y의 앵커(정박) 노드로 불린다. 전역적으로, 이전부터 알고 있던 해시함수 h(x)는 x의 구분자를 곶고 있는 피어를 위해 앵커 노드의 주소를 계산한다. 이 해시 함수 h(x)에 따라 새로운 노드들은 그들의 주소들을 그들의 앵커 노드들에게 요구하는 피어들에게 교대로 전파한다. 만약 주소가 h(x)에 의해 비어있다고 반환된다면, 그때 가장 적게 수정된 거리의 주소가 선택되고, 이것의 해당하는 피어가 앵커노드가 된다.


동적 주소 할당

 노드가 네트워크에 가입 할 때, 이것은 적당한 주소를 얻을 필요가 있다. 이것의 이웃들로부터 주기적인 라우팅 업데이트를 들으면서, 새로운 노드들은  가장 크게 비어 있는 주소 단위를 찾고, 그 단위로부터 주소를 고른다. 확실히, 이 the Prefix Subgraph Constraint는 만족스럽다. 그러나, 서로 보이지 않는 노드들이 네트워크에 가입하고 같은 앞자리 고정자를 고르게 된다면 어떤 일이 벌어질까? 이런 경우에, the Prefix Subgraph Constraint는 훼손당하게 된다. 주기적인 라우팅 업데이트를 통해, 그들의 조소들은 네트워크에 전파된다. 그 주소 서브트리로부터 모순되는 정보를 받는 가장 첫 노드는, 서브트리의 인식자(Identifier)와 비교하고, 더 높은 ID를 갖고 서브 트리를 떨어뜨린다. 큰 주소 공간은 주소 혼잡에 의한 새로운 노드의 요청의 거절을 방지하기 위해 사용된다. 더욱이, 주소트리의 균형은 the Prefix Subgraph Constraint의 위반없이 매우 혼잡한 주소공간에서 노드를 이동시키는 것 입니다. 결국, 높은 정도의 혼잡은 주소 공간 확장으로 이끌어 갈 수 있습니다.

 (출처 SarWiki -http://sarwiki.informatik.hu-berlin.de/DART_-_Dynamic_Address_Routing)




조금 쉽게 요약을 해보자면.

 IP주소를 이용하여, 상대 노드(단말)을 찾는 것이 아니라, 노드 별로 라우팅 테이블을 세팅하고 주기적으로 그 라우팅 테이블을 업데이트 하는 것이 핵심이다. 위의 내용은, 그것을 실현하는 개념적인 방법과, 그것의 이점을 정리해 놓은 것이다.

 이전에 사용하던 애드훅 라우팅 구조는, 정적인 주소 체계를 사용함으로써, 애드훅 네트워크 상에 있는 노드들이 많아지면 많아질수록, 헤더가 커지게 되고, 노드가 100개가 넘어가면 효율이 급격하게 떨어진다. 하지만 DART의 경우, 라우팅 테이블이 <Identifier, 주소>의 쌍으로 되어 있고, 이것을 기반으로 목적 노드를 찾아간다. 그리고 이것이 정적이 아닌, 동적으로 되어 있고 라우팅 테이블이 분산 되어 있어 광범위한 애드훅 구조라고 해도 효율적으로 관리가 가능하다는 장점이 있다.

 하지만, 만약 이 <Identifier, 주소>의 prefix가 겹치는 경우, 문제가 발생할 수 있다. 물론, 이것을 해결이 가능하지만, 이경우 End-to-End Delay가 발생하게 된다.


우분투(코분투) 10.04 버전 이상에서, Sun JDK를 설치 할 경우.
sudo apt-get install sun-java6-jdk
명령어로 하면, 안된다.

보통, 저렇게 하면, 됬었는데 10버전 이상에서는 안된다.
그러면, 어떻게 해야 되는지 찾아 보았다.

sudo add-apt-repository 

를 이용해서 서버를 추가 하면된다.
그래서 2~3개 해봤는데 안된다.

물론, 
sudo apt-get update는 해준다.

하지만 안된다.

결국, 불굴의 한국인으로서 해결책은 찾았다.

결과적으로 말하면, 서버가 바뀌었다. 그래서 백날 옛날 서버 주소를 repositroy에 추가 해봤자 안됬던거다.

그러므로 뭘 치면 되느냐?

sudo add-apt-repositroy ppa:ferramroberto/java

이거다.

후... 2시간동안 이것때문에 별짓을 다 했네.


설치환경 : 노트북, 코분투 11.04버전. 
오랜만에 올리는 프로그래밍 이다.
몇일에 걸쳐, 자바 기본문법 을때고, 이제 슬슬 졸업 작품을 준비하기 위해 통신 프로그램등을 공부하게 되었다. 그중에 가장 간단한 소켓 프로그램에 대해 포스팅 해보도록 하겠다.

우선 코드를 보자.



자잘한건. 뭐 넘어가도록 하고. 
여기서  신경 쓸 것은.

ServerSocket
Socket
socket.getInputStream();
DataInputStream
socket.getOutputStream();
DataOutputStream
Scanner scan = new Scanner(System.in);

din.readUTF();
dou.writeUTF(msg); 


뭐 이정도 일까?

우선 소켓 프로그래밍이란 것은. 내가 이해한 용도는 '버퍼' 이다. 스트림하고도 연관이 되겠지.
중간에 완충 역할을 해주는 것이라고 이해를 해준 것이다.
예전에 TCP/IP 소켓프로그래밍이란 책을 보면서 약간 이해를 했지만, 그래도 그저 아직은 중간 완충 해주는 '서비스'의 일종으로 이해하고 있다. 그정도면 아직까진 충분하지 않겠는가?

자, 그리고 InputStream과 OutputStream. 이 내용은 자바 기본 문법중 stream에 관한 것에대해서 공부를 해야 한다. 기본적인 바이트코드를 DataInputStream/DataOutputStream을 이용해서 우리가 읽을 수 있게 해준다고 생각해 주면 된다.

 잡 이야기는 여기까지 하도록 하고, 서버용 프로그램을 보도록 하자.
 우선 클라이언트와는 다르게 서버에서는 ServerSocket을 사용한다. 서버로서의 소켓을 정해줘야 갑과 을을 정해서 누가 주동적으로 역할을 수행할 지를 정하는 것이다.
 그리고 API 문서에 보면 수많은 생성자가 있지만, 이 프로그램에서는 그냥 9999라는 포트만을 넘겨줘서 생성해 주고 있다.
 그리고 나온 매소드중 accept()라는 매소드는. 다른 소켓이 server에 접속하기까지 포트를 열고 대기한다.  그리고 해당하는 새로운 소켓을 return 한다.

 그리고 din.readUTF()가 뭔가 하면, 우선 InputStream에 소켓의 getInputStream 메소드를 넘긴다. 소켓의 스트림에 들어있는 내용을 반환한다. 그리고 그것을 데이터 스트림에 대입하여 인간이 읽을 수 있도록 변환한다.

 아웃풋도 마찬가지의 이야기이다.

 거기에 있는 데이터를 UTF형식으로 하여 msg에 대입하라는 내용인 것이다.

 그리고 만약 그 내용이 exit면, dou.writeUTF("END") 즉, 클라이언트 측에 END라는 문자를 날리고 소켓 종료, 서버소켓 종료하고 반복문을 종료-> 프로그램 종료 를 하라는 이야기가 완성이 된다.

 만약 아니라고 한다면, server측에서도 하고 싶은 이야기를 건네 클라이언트 측에 전달하는 것이지 :) 

이번에는 클라이언트 측을 살펴보도록 하자. 



 가장 처음에 보면, 서버용 IP를 넣는 것이 보인다. 이것을 args로 받아도 되고, 아니면 따로 입력받도록 고쳐도 좋을 것 같다. 하지만, 우선 실험용이기 떄문에 임의로 입력을 했다.
 그리고 소켓을 생성한다. 앞에서 만들어 놨던 IP와 포트 9999를 이용한다.

 그 후 한 문장을 입력 받고, 서버에 전송한다. 그리고 대기 하다가 서버측에서 메시지가 도착하면 메시지에 디스플레이 하고, 그 문장이 (END)가 아니라면, 반복문을 계속하여 메시지를 전달한다.





사실 서버 측에서 쓰레드를 이용한 FULL duplex를 만들고 싶었으나, 콘솔용에서 FULL duplex는 뭔가 안맞다 싶었다. 완전히 만지려면, 콘솔 프로그램 자체를 바꿔야 한다.
 뭐, 버퍼를 이용해서 하는 방법이 있지만......... 자, 어서어서 다음꺼 해야지^^ㅋ 

'Programming > JAVA' 카테고리의 다른 글

우분투에서 SUN JDK 설치.  (0) 2012.03.01
HBRUSH CreateSolidBrush(COLORREF crColor);
HBRUSH CreateHatchBrush(int fnStyle, COLORREF crColor);

위에는 그냥 단색의 브러쉬
아래는 색칠하는 스타일을 정할수 있다.
HS_BDIAGONAL : 좌하향 줄무늬
HS_CROSS 바둑판
HS_DIAGCROSS 좌하향 및 우하향
HS_FDIAGONAL 우하향
HS_HORIZONTAL 수평선
HS_VERTICAL 수직선

'Programming > Visual C++' 카테고리의 다른 글

그래픽3. PEN  (0) 2011.06.30
그래픽2. StockObject  (0) 2011.06.30
그래픽 1. GDI Object  (0) 2011.06.30
기타. SendMessage - Message 강제 호출  (0) 2011.06.30
타이머.  (0) 2011.06.29

HPEN CreatePen(int fnPenStyle,int nWidth,COLORREF crColor);

fnPenStyle은 점선이냐 실선이냐, 실선은 얼마나 할꺼냐의문제. 단, nWidth!=1, PS_SOLID가 자동으로 선택된다.

PS_SOLID
PS_DASH
PS_DOT
PS_DASHDOT
PS_DASHDOTDOT


nWidth는 굵기.
1이 기본값이고
2,3으로 하면 두꺼워진다. 0으로 하면 무조건 1 pixel의 두께.

crColor
선의 색상을 정한다. COLORREF형. RGB매크로 함수를 사용하면 된다.

다음은 예제

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("First");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;

 WndClass.cbClsExtra = 0;
 WndClass.cbWndExtra = 0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style = CS_HREDRAW|CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);

 while(GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 HPEN MyPen, OldPen;

 switch(iMessage)
 {
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  MyPen=(HPEN)CreatePen(PS_SOLID,5,RGB(0,0,255));
  (HBRUSH)SelectObject(hdc,GetStockObject(GRAY_BRUSH));
  SelectObject(hdc,MyPen);
  Rectangle(hdc,50,50,300,200);
  DeleteObject(MyPen);
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY : PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

여기서 한가지. CreatePen을 한경우에는 메모리를 할당해준것이기 때문에 DeleteObject를 해주어야한다.

'Programming > Visual C++' 카테고리의 다른 글

그래픽4. Brush  (0) 2011.06.30
그래픽2. StockObject  (0) 2011.06.30
그래픽 1. GDI Object  (0) 2011.06.30
기타. SendMessage - Message 강제 호출  (0) 2011.06.30
타이머.  (0) 2011.06.29

GDI에서 기본적으로 제공하는 Object.

HGDIOBJ GetStockObject(int fnObject);

BLACK_BRUSH
GRAY_BRUSH
NULL_BRUSH(투명)
WHITE_BRUSH
DKGRAY_BRUSH
LTGRAY_BRUSH
DC_BRUSH(색상 브러시, SetDCBrushColor로 색 설정)
BLACK_PEN
WHITE_PEN
NULL_PEN
DC_PEN(색상 펜, SetDCPenColor 함수로 설정)
ANSI_FIXED_FONT 고정폭 폰트
ANSI_VAR_FONT 가변폭 폰트
DEFAULT_PALETTE 시스템 기본 팔레트


사용 예

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("First");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;

 WndClass.cbClsExtra = 0;
 WndClass.cbWndExtra = 0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style = CS_HREDRAW|CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);

 while(GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 HBRUSH MyBrush,OldBrush;

 switch(iMessage)
 {
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  (HBRUSH)SelectObject(hdc,GetStockObject(GRAY_BRUSH));
  Rectangle(hdc,50,50,300,200);
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY : PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

'Programming > Visual C++' 카테고리의 다른 글

그래픽4. Brush  (0) 2011.06.30
그래픽3. PEN  (0) 2011.06.30
그래픽 1. GDI Object  (0) 2011.06.30
기타. SendMessage - Message 강제 호출  (0) 2011.06.30
타이머.  (0) 2011.06.29
GDI(Graphic Device Interface) Object. 화면, 프린터등의 모든 출력 장치를 제어하는 윈도우즈 핵심 모듈중 하나.
 EX) 펜, 브러시, 비트맵, 폰트 등.

DC가 일종의 GDI를 모아놓은 것.
사용자는 DC를 사용할때 적절한 Object를 선택해서 사용할수 있다.(적절한 펜, 브러쉬, 폰트 등)
만약 선택하지 않으면 알아서 Default로 선택되는 것들도 있다.

펜 HPEN 선을 그릴때 사용, (Default)검정색의 가는 실선
브러시 HBRUSH 면을 채울때 사용 (Default) 흰색
폰트 HFONT 문자 출력에 사용되는 글꼴(Default)시스템 글꼴
비트맵 HBITMAP 비트맵 이미지 (Default)없음
팔레트 HPALETTE 색상을 선택할수 있는 팔레트 (Default)없음


'Programming > Visual C++' 카테고리의 다른 글

그래픽3. PEN  (0) 2011.06.30
그래픽2. StockObject  (0) 2011.06.30
기타. SendMessage - Message 강제 호출  (0) 2011.06.30
타이머.  (0) 2011.06.29
기타. WM_CREATE  (0) 2011.06.29
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam,LPARAM lParam);
강제로 메시지가 발생한것처럼 만든다.
Timer같은경우에도 함수 발생후 1초가 지나야 메시지가 발생하므로 그것을 강제로 하기위해서 필요하다.

case WM_CREATE:
SetTimer(hWnd,1,1000,NULL);
SendMessage(hWnd,WM_TIMER,1,0);

요롷게 해주면된다.

'Programming > Visual C++' 카테고리의 다른 글

그래픽2. StockObject  (0) 2011.06.30
그래픽 1. GDI Object  (0) 2011.06.30
타이머.  (0) 2011.06.29
기타. WM_CREATE  (0) 2011.06.29
입력5. 마우스  (0) 2011.06.29


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 SYSTEMTIME st;
 static TCHAR sTime[128];

 switch(iMessage)
 {
 case WM_CREATE:
  SetTimer(hWnd,1,1000,NULL);
  return 0;
 case WM_TIMER:
  GetLocalTime(&st);
  wsprintf(sTime,TEXT("지금 시간은 %d:%d:%d입니다."),st.wHour,st.wMinute,st.wSecond);
  InvalidateRect(hWnd,NULL,TRUE);
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  TextOut(hdc,100,100,sTime,lstrlen(sTime));
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY:
  KillTimer(hWnd,1);
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

자, 여기서
UINT SetTimer(HWND hWnd,UINT nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc);
에 대해 알아보도록하자.

nIDEvent는 타이머의 번호를 지정한다.
uElapse는 타이머의 주기를 설정한다. 단위는 1/1000초. == 1초에 한번씩 타이머 메시지가 hWnd로 전달된다.
10000이면 10초에 한번씩 타이머 메시지가 발생한다.
TIMERPROC lpTimerFunc는 타이머 메시지가 발생할 때마다 호출될 함수를 지정. 사용하지 않을경우 NULL.

'Programming > Visual C++' 카테고리의 다른 글

그래픽 1. GDI Object  (0) 2011.06.30
기타. SendMessage - Message 강제 호출  (0) 2011.06.30
기타. WM_CREATE  (0) 2011.06.29
입력5. 마우스  (0) 2011.06.29
입력3. TranslateMessage  (0) 2011.06.29
WndProc에서 첫 번째로 처리하는 메시지로 윈도우가 처음 생성될 때 발생, 초기화에 이용.

'Programming > Visual C++' 카테고리의 다른 글

기타. SendMessage - Message 강제 호출  (0) 2011.06.30
타이머.  (0) 2011.06.29
입력5. 마우스  (0) 2011.06.29
입력3. TranslateMessage  (0) 2011.06.29
입력2. WM_KEYDOWN  (0) 2011.06.29
다음은 마우스 입력 메시지다. 직관적이라 잘 알수 있을듯.

WM_LBUTTONDOWN
WM_RBUTTONDOWN
WM_MBUTTONDOWN
WM_LBUTTONUP
WM_RBUTTONUP
WM_MBUTTONUP
WM_LBUTTONDBLCLK
WM_RBUTTONDBLCLK
WM_MBUTTONDBLCLK


 그리고 여기서 하나더, 마우스 메시지는 lParam의 상위 워드에 마우스 버튼이 눌러진, y좌표, 하위 워드에 x좌표를 가지며 좌표값을 검출해 내기 위해 HIWORD, LOWORD등의 매크로 함수를 사용한다. 즉 마우스 메시지가 발생한 위치의 조표는  (LOWORD(lParam),HIWORD(lParam))이 된다.

 또한 다중모니터의 경우 이 좌표값이 음수가 될수도 있다.

wParam에는 마우스 버튼의 상태와 키보드 조합키의 상태가 전달된다.

MK_CONTROL Ctrl버튼이 눌러져 있음
MK_LBUTTON 마우스 왼쪽버튼이 눌러져 있음.
MK_RBUTTON 마우스 오른쪽 버튼이 눌러져 있음.
MK_MBUTTON 마우스 중간버튼이 눌러져 있음.
MK_SHIFT Shift버튼이 눌러져 있음.

이외에도 마우스가 움직일때 전달되는 WM_MOUSEMOVE, 휠마우스 상태를 정해주는 WM_MOUSEWHEEL등이 있다.

'Programming > Visual C++' 카테고리의 다른 글

타이머.  (0) 2011.06.29
기타. WM_CREATE  (0) 2011.06.29
입력3. TranslateMessage  (0) 2011.06.29
입력2. WM_KEYDOWN  (0) 2011.06.29
입력1. WM_CHAR  (0) 2011.06.29
키보드에서 A라는 글자가 눌렀다 뗏을 경우.

발생하는 메시지는 순서대로
WM_KEYDOWN, WM_CHAR, WM_KEYUP이 발생한다.
하지만 사실 WM_CAHR는 사용자로부터 발생한것은 아니다. WM_KEYDOWN이 오면, 그값을 토대로 WM_CHAR를 발생시킨다. 이 발생시키는 것이 TranslateMessage다.
 이 TranslateMessage라는 놈은 많은 걸 보고 판단해서 WM_CHAR를 발생시킨다. 예를들어 눌렀을때의 Shift키라던가 Caps Lock라던가, 현재 운영체재의 언어설정이라던가 말이다.

'Programming > Visual C++' 카테고리의 다른 글

기타. WM_CREATE  (0) 2011.06.29
입력5. 마우스  (0) 2011.06.29
입력2. WM_KEYDOWN  (0) 2011.06.29
입력1. WM_CHAR  (0) 2011.06.29
출력5. MessageBox & MessageBeep  (0) 2011.06.28

WM_CHAR가 문자만 받는다면 WM_KEYDOWN은 문자이외에도 키보드가 눌리면 발생하는 이벤트 메시지다. 각각의 값은 다른데서 찾아보시고.
wParam값에 키보드가 눌러진 값이 반환이 된다.

다음은 WM_KEYDOWN의 예제이다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 static int x=100;
 static int y=100;

 switch(iMessage)
 {
 case WM_KEYDOWN:
  switch(wParam)
  {
  case VK_LEFT:
   x-=8;
   break;
  case VK_RIGHT:
   x+=8;
   break;
  case VK_UP:
   y-=8;
   break;
  case VK_DOWN:
   y+=8;
   break;
  }
  InvalidateRect(hWnd,NULL,TRUE);
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  TextOut(hdc,x,y,TEXT("A"),lstrlen(TEXT("A")));
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
  }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
 }


그리고 다음 예제는 스페이스가 눌리면 A와 #이 토글되는 예제이다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 static BOOL sp_in=FALSE;
 static int x=100;
 static int y=100;
 LPCTSTR a=TEXT("A");

 switch(iMessage)
 {
 case WM_KEYDOWN:
  switch(wParam)
  {
  case VK_LEFT:
   x-=8;
   break;
  case VK_RIGHT:
   x+=8;
   break;
  case VK_UP:
   y-=8;
   break;
  case VK_DOWN:
   y+=8;
   break;
  case VK_SPACE:
   sp_in=!sp_in;
   break;
  }
  InvalidateRect(hWnd,NULL,TRUE);
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  if(sp_in==FALSE)a=TEXT("A");
  else if(sp_in==TRUE) a=TEXT("#");
  TextOut(hdc,x,y,a,lstrlen(a));
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
  }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
 }

'Programming > Visual C++' 카테고리의 다른 글

입력5. 마우스  (0) 2011.06.29
입력3. TranslateMessage  (0) 2011.06.29
입력1. WM_CHAR  (0) 2011.06.29
출력5. MessageBox & MessageBeep  (0) 2011.06.28
출력4. 기타  (0) 2011.06.28

 일반적으로 도스에서 입력하는걸 생각해보자. 그러면 생각할것없이 키보드가 갖고 있는 고유의 값을 받아서 프로그램에서 문자로 해석해 모니터로 쏴주면 된다.
 그럼 윈도우에서는 어떠한가? 멀티테스킹이란 것으로 윈도우의 여러개의 창들이 있고 내가 실행하고 있는 프로그램중에서도 내가 원하는 위치에 글자를 출력해야 한다.
 이렇게 어디에 글자를 출력할것인지 선택하는걸 "포커스"라고 한다.
 키보드에서 입력받은 문자는 "활성화"된 프로그램의 CP부터 글자가 출력이 된다.(기본적으로)

 활성화를 "선택" 이라고 보면 되고 포커스를"집중"이라고 생각하면 쉽다.

자, 그럼 WM_CHAR의 활용을 보자.

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;
 static TCHAR str[256];
 int len;

 switch(iMessage)
 {
 case WM_CHAR:
  len=lstrlen(str);
  str[len]=(TCHAR)wParam;
  str[len+1]=0;
  InvalidateRect(hWnd,NULL,FALSE);
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  TextOut(hdc,100,100,str,lstrlen(str));
  EndPaint(hWnd,&ps);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}


여기서 집중해야 할것은 음...
 case WM_CHAR:
  len=lstrlen(str);
  str[len]=(TCHAR)wParam;
  str[len+1]=0;
  InvalidateRect(hWnd,NULL,FALSE);
  return 0;

이정도면 충분할것 같군.

대충 해석을 해보자면, 정수형 len에 str의 현재 길이를 구한다.
가장 처음 상태에선 아무것도 없는 0이 반환이 되겠지?
그럼 str[0]에 wParam글자가 온다.
여기서 WM_CHAR메시지는 은 입력된 문자의 코드를 wParam으로 전달한다.

여담으로 lParam도 한번 보고 가자. WM_CHAR의 경우 lParam에는 비트별로
0~15비트에는 반복카운트,
16~23 스캔코드
24 : 오른쪽 Alt, 오른쪽 Ctrl등 101키에만 있는 확장키가 눌러졌을경우 1
25~28 : 미사용
29 : Alt키가 눌렸으면 1
30 : 메시지가 보내기전에 키가 눌러져 있었으면 1
31 : 키가 놓아지면 1, 눌러지면 0

자 여담은 여기서 마치도록 하고

  len=lstrlen(str);
  str[len]=(TCHAR)wParam;
  str[len+1]=0;

이걸 조금더 자세히 보자면,
str[0]에 글자가 들어가고 그다음 배열에 널을 넣어 문자의 종료를 알린다.
그리고 만약또다시 뭔가가 들어온다싶으면 lstrlen(str)==1 이되므로.
str[1]에 글자가 넣어진다. 그리고 str[2]에 널문자가 오게 되겠지.
한글자 한글자씩 문자를 넣고 널문자를 하나씩 미루는 형식으로 되어있다.

자, 여기서 끝내고 글자가 짠! 하고 나타나면 좋겠지만, 하나 부족한것이 있다면, 출력하는 코드가 보이지 않는다. 화면에 표시하는코드는? 아, WM_PAINT에 있군. 그렇다면 WM_PAINT를 호출하는 시점은?

1. 프로그램을 시작할때.
자, 프로그램을 시작할때 str은 비어있다. 아무리 글자를 쳐봐도 화면에 나오지 않는 이유가 그것이다.
호출하는 시점이 틀려먹었잖아. 그렇다면 또 언제 호출을 하지?

2. 화면을 다시 그릴때.
 화면을 다시 그릴땐 WM_PAINT에 저장되있는것이 다시 호출이 된다. 음.. 언제 다시 호출이 되지? 윈도우창이 가려지고 다시 나타날때 다시 그려진다.

자, 이 화면을 다시그리라는 명령을 해주는 함수가 있으니

  InvalidateRect(hWnd,NULL,FALSE);

요 함수다.
이 함수는 윈도우의 작업영역을 무효화 하여 운영체제가 WM_PAINT메시지를 다시 호출하도록 한다.
첫번째 hWnd는 그 대상이 된다. 뭐, 다른 윈도우를 대상으로 할 수도 있다.
그다음은 CONST RECT *lpRect인데. 무효화할 사각형을 지정한다. 이 값이 NULL값이면, 윈도우의 전영역이 무효화 된다. 하지만 NULL값으로 두면 그릴것이 많아질수록 속도가 느려질수 있는 단점이 있다.
세번째는 BOOL bErase로 무효화되기 전에 배경을 모두 지운후 다시 그릴 것인지 아니면 배경을 지우지 않고 그릴것인지를 지정한다. TRUE면 배경을 지운호 다시그리고, FALSE면 배경을 지우지 않고 다시 그린다.

'Programming > Visual C++' 카테고리의 다른 글

입력3. TranslateMessage  (0) 2011.06.29
입력2. WM_KEYDOWN  (0) 2011.06.29
출력5. MessageBox & MessageBeep  (0) 2011.06.28
출력4. 기타  (0) 2011.06.28
출력3. DrawText  (0) 2011.06.28
MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCation, UINT uType);
핸들값, 메시지 박스에 넣을 글자, 타이틀에 나올글자, 메시지 박스에 나타날 버튼의 종류

MB_ABORTRETRYIGNORE Abort, Retry Ignore 세개의 버튼이 나타난다.
MB_OK, OK버튼 하나만 나타난다.
MB_OKCANCEL, OK, CANCEL 두개의 버튼이 나타난다.
MB_RETRYCANCEL, Retry, Cancel 두개의 버튼이 나타난다.
MB_YESNO
MB_YESNOCANCEL

이것뿐아니라 아이콘또한 표시할 수 있다.
MB_ICONEXCLAMATION,MB_ICONWARNING.
MB_ICONINFORMATION,MB_ICONASTERISK.
MB_ICONQUESTION
MB_ICONSTOP, MB_ICONERROR,MB_ICONHAND

그리고 이 MessageBox의 리턴값은 사용자가 누른 버튼값을 되돌려준다.
IDABORT Abort 버튼을 눌렀다.
IDCANCEL
IDIGNORE
IDNO
IDOK
IDRETRY
IDYES

EX)
if(MessageBox(hWnd,Text("응? 뭐라고?"),TEXT("야!!"),MB_YESNO==IDYES){}
else{}

요롷게;



BOOL MessageBeep(UINT uType);
비프음을 내는 함수
0xffffffff  pc스피커를 통해 음을 낸다.
MB_ICONASTERISK Asterisk 비프음
MB_ICONEXCLAMATION Exclamation 비프음
MB_ICONQUESTION Question 비프음
MB_OK 시스템 디폴트 비프음.





'Programming > Visual C++' 카테고리의 다른 글

입력2. WM_KEYDOWN  (0) 2011.06.29
입력1. WM_CHAR  (0) 2011.06.29
출력4. 기타  (0) 2011.06.28
출력3. DrawText  (0) 2011.06.28
출력2. WM_Paint  (0) 2011.06.28

+ Recent posts