멍하니 계단을 걸어내려오면서 가서 해야할 일, 그리고 사야할 목록을 생각해봤다.


몇 가지 안되는 것들이지만 자꾸 되뇌다보니 내가 뭔가 하나를 빠트리고 있는 것 같은 불안감이 몰려왔다. 


건물의 현관을 벗어나서 이미 어두워진 아스팔트를 걷다보니 무거워진 다리에 힘이 풀리고, 발을 힘들게 내딛으며 지구의 중력과 몸이 앞으로 향하는 관성에 이끌려 걷고 있는 나를 느꼈다.


'뭐, 괜찮을 거야.'


막연하게 중얼거리는 것에 대한 확신이나 증거는 없었지만 별다른 도리가 없다.


그간의 시간 혹은 희미해진 기억, 지금의 내가 살고 있는 현실과 지나온 과거가 아주 작은 불빛마냥 타고 있는 것 같다.


살아있는 나는 바로 앞의 비춰진 길의 한 걸음까지만 볼 수 있는 자그맣고 희미한 빛이다.


시간이 빚은 작고 흔들리는 희미한 빛.



붉은 벽돌로된 담을 걷다가 라일락 향기에 잠시 고개를 들어본다.


어두운 밤 하늘을 배경으로 담벼락 너머의 하얗게만 보이는 작은 꽃들이 모여 진한 향기를 뿜어대고 있다.


너무 가까이가면 머리가 아플 정도로 강렬한 향기가 코를 자극하고, 몇 걸음 떨어져 있으면 은은하게 향을 맡을 수 있다.


거리가 주는 향의 깊이, 강렬함, 화사함.


사람없는 골목에서 행여 내 작은 불빛에 향기 담길까 싶어서 담을 살짝 넘어온 라일락 나무 아래 서서 나무를 올려 본다.


4월의 라일락이 핀 담 아래서 그렇게 흔들리고 있다.




콘솔게이머인 나를 스팀으로 이끌어준 스팀비비가 문을 닫는다고 한다.


사실 게임을 사고, 모으고, 현물인 팩과 디스크를 쌓아올리는데 지친지 꽤 되었다.


비비를 알게되고 난 뒤인 플스3 이후로는 콘솔 게임기도 구매하지 않게 되었고, 스마트폰이 들어온 뒤로는 휴대 게임기도 구매가 쉽게 되지 않았다.


온라인 라이브러리는 늘어만가고...게임을 구매하면 반드시 한다...는 것도 스팀을 하면서 깨져버리게 되었고, 2천개가 넘어가면서 비비가 아니면 구매한 게임인지 확인하는 것도 어렵게 되었다.


번들이 나올 때마다 구매했던 게임인지 확인하는 것도 어려울 지경...


아무튼...비비가 사라진다고 한다.


스팀 게이머가 된 지 몇 년 안되었지만 안타까운 마음 뿐이다.


얼마전에 아스카 피겨를 구했는데...얼굴과 힙을 동시에 볼 수 있었다.

이런 신박한 피겨 같으니라구.

보고 싶은 욕망을 동시에 해결해주는 피겨라니.







근데, 오버워치의 승리포즈를 보니 어디선가 본 포즈...


크으...아스카, 아스카, 아스카







야구 시즌이 돌아왔다.



개발을 시작한 지 3년이 조금 넘은 시점이었던 3년 전 겨울에 응용 프로그램의 개발이 어떻게 보면 업무에 따른 패턴이 정해져 있는 단순 작업이라는 것을 느끼게 되었다.


특정 업무에서 요구하는 메뉴 구성과 기능이 어느 정도 정해져 있던 것인데, 이런 작업을 하면서 응용 프로그래머의 영역은 이제 개발 머신이 대체하겠구나 하는 생각이 들었다.


그만큼 응용 프로그래밍의 영역은 어떻게 보면 진입장벽이 꽤 낮고, 실질적으로 사용하기 쉬운 하이레벨 API를 이용한 인터페이스와 일부 라이브러리의 조합이라는 인상이 있었던 탓이었을지 모른다.


어제 오늘 알파고와 이세돌의 대국을 보면서 그 가능성이 열려있고, 빠르게 진입하고 있다는 인상도 받았다.


다소 제목은 과장되었지만 개발 업무를 위해서 몇 개월씩 응용 프로그래머를 고용하는 시절이 점차 사라지게 될 것 같은 생각이 든다.


갑자기 시작한 응용 프로그래머의 삶이 얼마나 더 진행될지 모르겠지만 뭔가 쓸쓸한 그리고 쌀쌀한 날씨의 봄 저녁이다.





2016.03.13 업데이트



이세돌 구단이 알파고를 불계승으로 이겼다...3패 뒤 1승...바둑은 잘 모르지만 재미있다.


문서 - http://www.egovframe.go.kr/wiki/doku.php



웹 개발자가 아닌 앱 응용개발자로 먹고 사는 관계로 웹은 다른 작업자가 대부분하고 있지만, 한 번 알아볼까해서 전자정부 프레임워크의 개발환경을 한 번 설정해보았다.

작년 초에 한 번 문서보고 설정해보니 잘 굴러가서 그런가보다 했는데, 오늘 해보니 조금 꼬인다.

3.5.1이 최신이길래 이를 기반으로 문서를 보고 따라하기 식으로 해봤는데, 문서가 좀 뒤죽박죽이다.

일단 이클립스는 케플러로 변경한 뒤의 인터페이스가 스샷과 일부 다르고, 로컬 메이븐 폴더 및 settings.xml 설정의 이미지는 기존 배포 버전 기준으로 그대로이고, 샘플 프로젝트가 JDK 1.8에서 잘 실행이 안됐다. (문서 상에는 1.7 이상 지원이라고 했지만 실제 1.8에서는 정상 작동하지 않는 것으로 생각된다.)

이 문서를 읽는 대상이 어떤 설정에 문제가 있는 것인지, 문제가 있을 경우 해결 방법에 대해 아예 모르는 상태이므로 초기 설정 가이드가 중요하다고 생각되는데 생각 외로 업데이트가 바로 반영이 되는 것은 아닌 것 같아보인다.

설정이라는 것이 한 두가지 놓치고 나면 처음부터 다시 따라가야 하는 경우도 꽤 많아서 좀 번거롭더라도 초보 가이드의 진입 부분은 다소 자세하고 정확한 설명이 필요할 것 같다.

아무튼, JDK는 1.7로 다시 설치하고, 환경변수 잡은 뒤 이클립스 기본 환경도 같이 변경을 해주었다.

그랬더니...기대한 대로 샘플들이 정상적으로 돌아간다.

에러 표시가 몇 개 나기는 하지만 검색해서 수정하면 되지 않을까 싶다.
(Conflicting lifecycle mapping 같은 에러 메시지였는데, pom.xml 파일의 플러그인 태그에 라이프사이클 관련 태그를 넣어도 없어지지 않았다. 좀 찾아보니 해당 에러는 3.5.1 배포 파일의 m2e 버전이 1.6.0이나 1.6.1이 설치되어서 그런 것 같다. m2e 업데이트를 진행하니 관련 에러 메시지는 바로 사라졌다.)


웹 쪽 개발에 대한 지식이 전무하기 때문에 전체적인 문서를 한 번 일독한 뒤에 개발, 배포, 운영에 대한 부분을 하나씩 습득하여야 겠다.



제어문이 크게 차이는 없지만 생소하기는 하니 플레이 그라운드를 한 번 실행해서 한 번씩 적어본다.



1. for 문


for var i=0; i<10; i++ {

    print("value of i is \(i)");

}


for index in 1...10 {

    print("index is \(index)")

}


이런 형태를 for-in 문이라고 한다. 어레이 형태의 데이터 셋에서 하나씩 비교하여 값을 읽어들일 수 있다.


var count = 0;

for _ in 1...10 {

    count++;

}


언더바(_)는 반복문에서 참조 변수가 필요하지 않을 경우 넣을 수 있다.



2. while 문


var cnt = 0;

while cnt < 100 {

    ++cnt;

}



3. repeat-while 문


var i = 10;

repeat {

    --i;

}

while (i>0)


응? do-while 문이 안보이네...플레이 그라운드에서 자동으로 repeat-while 문으로 변경된다.


반복문의 제어를 위해서 break와 continue 도 동일하게 적용된다.



4. if 문


var a = 10;

if a>9 {

    print("9보다 크다.")

}


if-else 문은 다음과 같이 사용할 수 있다.

if a<=9 {

    print("9보다 작거나 같다.")

}

else if a>10 {

    print("9보다 크다.")

}


API를 체크하는 기본적인 기능이 담겨 있다. 
특정 API를 체크해서 실행이 필요한 경우가 있는데, 다음과 같이 사용한다. 

if #available(iOS 9, OSX 10.10, *) {

    // Use iOS 9 APIs on iOS, and use OS X v10.10 APIs on OS X

} else {

    // Fall back to earlier iOS and OS X APIs

}



5. switch 문

var j = 10

switch (j) {

    case 10:

        print("값은 10");

    case 20:

        print("값은 20");

    default:

        print("10 20 아니다.");

}


일반적인 스위치문은 동일하다. 다음처럼 범위를 지정할수도 있다.

var j = 10

switch (j) {

case 1...10:

    print("값은 1부터 10 사이");

case 11...20:

    print("값은 11부터 20 사이");

default:

    print("1부터 20 사이의 값이 없다.");

}


범위 설정과 where 문 사용으로 좀 더 유연하게 설정할 수 있다.

switch (j) {

case 1...10 where j%2 == 0:

    print("값은 1부터 10 사이의 짝수");

case 11...20 where j%2 == 0:

    print("값은 11부터 20 사이의 짝수");

default:

    print("1부터 20 사이의 값이 없다.");

}


눈치 챘겠지만 break 문이 없다. 그냥 해당 조건을 만나면 케이스문을 실행하고 스위치 문을 종료한다.
이 break 문을 없앤 일반 스위치문과 동일하게 구현하려면 fallthrough 문을 넣어준다.

switch (j) {

case 1...10 where j%2 == 0:

    print("값은 1부터 10 사이의 짝수")

    fallthrough

case 11...20 where j%2 == 0:

    print("값은 11부터 20 사이의 짝수")

    fallthrough

default:

    break    

}


튜플 데이터를 통해서 스위치문을 비교할 수도 있다.


let somePoint = (1, 1)

switch somePoint {

case (0, 0):

    print("(0, 0) is at the origin")

case (_, 0):

    print("(\(somePoint.0), 0) is on the x-axis")

case (0, _):

    print("(0, \(somePoint.1)) is on the y-axis")

case (-2...2, -2...2):

    print("(\(somePoint.0), \(somePoint.1)) is inside the box")

default:

    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")

}


이렇게 임시 변수에 데이터를 바인딩하여 처리할 수도 있다.


let anotherPoint = (2, 0)

switch anotherPoint {

case (let x, 0):

    print("on the x-axis with an x value of \(x)")

case (0, let y):

    print("on the y-axis with a y value of \(y)")

case let (x, y):

    print("somewhere else at (\(x), \(y))")

}

// prints "on the x-axis with an x value of 2"




그냥 입력하다보니 세미콜론을 찍었다가 안찍었다가 했네...그런데, 에러 메시지가 없이 실행된다.

넣어도 되고 않넣어도 되나보다.



Objective-C를 처음으로 개발을 시작해서 그런지 조금 편하다고 느낀다.


하지만 앞으로는 스위프트를 해야 할 것 같다...만약에 계속 프로그램을 개발하게 된다면 말이다.


첫인상

처음 나왔을 때 간단한 코드를 보고 자바 스크립트 같다고 처음에는 느꼈다. 하지만 자바 스크립트와는 다른 듯...

마침표가 없어서 낯설다. 세미콜론이 있어야 문장이 끝나는 느낌인데...없으니 허전하다.


아무튼 첫인상이 주는 느낌은 간단해보이고 낯선 느낌이었는데, PlayGround를 하나 실행하여 줄줄이 쳐본다.



1. 데이터 타입


부호 있는 정수: Int를 쓰면 된다. (Int8, Int16, Int32, Int64)

부호 없는 정수: Uint를 쓰면 된다. (UInt8, UInt16, UInt32, UInt64)


소수: Float, Double 


부울: Bool (true와 false 값을 가진다)


문자: Character


문자열: String


특수문자: 문자열을 다루고 출력에서 많이 사용하는 특수문자는 다음과 같다.

 - \n: 줄 바꿈

 - \r: 캐리지 리턴

 - \t: 탭

 - \\: 역슬래쉬

 - \": 더블 쿼트

 - \': 싱글 쿼트


대충 이정도면 기본 코딩할 수 있겠다.



2. 변수와 상수


var myVariable = 10

let myConstant = 10


var를 붙이면 변수, let를 붙이면 상수가 된다.



3. 변수 선언 / 할당 / 사용 - 옵셔널(optional)


위에서는 선언과 할당을 같이 했다. 변수의 선언만 하고자 할 때에는 옵셔널 타입(optional type)을 사용한다.

변수명 뒤에 콜론을 찍고, 데이터 타입을 적어주는 것을 타입 어노테이션(Type Annotation)이라고 한다. 


var myVariable: Int?

let myConstant: Int?


이 시점의 변수에는 nil의 값을 가지고 있다. 여기에 값을 할당하여 변수가 값을 가지게 되면 랩핑되었다(wrapped)고 표현한다.


myVariable = 10

myConstant = 10


이 옵셔널 타입의 변수를 사용하면 unwrapped 되지 않았다는 메시지가 뜬다. 


print(myVariable)        // Optional(10) 


언랩핑하기 위해서는 해당 변수 뒤에 !를 붙여서 사용할 수 있도록 해주어야 한다.


print(myVariable!)                      // 10


강제로 언랩핑하는 방법이 하나더 있다. 옵셔널 바인딩(optional binding)을 통해서 할당된 값을 임시 변수나 상수에 할당한다.


if var tmpVariable = myVariable {

    print(tmpVariable)        // 10

}


if let tmpConstant = myConstant {

    print(tmpConstant)        // 10

}


이 옵셔널 바인딩은 우선 지정한 옵셔널 변수나 상수가 값을 가지고 있는 지 먼저 체크한다. 그 뒤에 값이 있을 경우 블럭 안의 코드를 실행하도록 한다.


'그런데, 왜 이래야 하나? 번거롭다.' 라고 생각하던 차에 암묵적인 언래핑(Implicitly unwrapped)이 있었다.


var myVariable: Int!

let myConstant: Int!


그렇다. 그냥 이렇게 !를 붙여서 선언하여 사용하면 변수나 상수를 사용 시에 언랩핑을 해줄 필요 없이 사용할 수 있다.


대충 이정도면 변수와 상수를 다룰 수 있겠다.



4. 신기한 튜플(Tuple)


데이터 타입이 다른 값들을 하나로 묶어서 쓸 수 있는 튜플이 있다.


let myTuple = (2, 2.123, "test")

var myTuple2 = (1, 1.1, "test2")

myTuple2 = (2, 2.1, "changed")        // 값이 변경된다.


해당하는 값을 읽어오려면 어레이처럼 0번 인덱스부터 읽어오면 된다.


print(myTuple.0)            // 2


튜플의 값을 하나 꺼내 비교도 할 수 있다.


if myTuple.0 == myTuple2.0 {

    print("\(myTuple.0)와(과) \(myTuple2.0) 값이 같아요.")        // 2와(과) 2의 값이 같아요.

}



튜플의 값을 일괄적으로 다른 변수에 대입할 수도 있다.


var (myInt, myFloat, myString) = myTuple


특정 값을 제외하고 대입할 수도 있다.  이 때는 해당 항목에 '_'(언더바)를 넣어준다.


var (myInt, _, myString) = myTuple


튜플 생성 시에 각 항목에 이름을 넣어줄 수도 있다. 마치 순서있는 딕셔너리(Ordered Dictionary) 같다.


let myTuple3 = (num: 10, height: 10.123, message: "test")


이 때는 값을 이렇게 읽을 수 있다.


print(myTuple3.0)            // 10

print(myTuple3.num)          // 10



책 10 페이지 정도 읽었는데...일단 이정도. 책은 대충보고 애플 문서를 일독해야 겠다.




앱을 만들다보면 아주 가끔이지만 텍스트 파일을 다뤄야 할 때가 있다.




1. 파일 생성 및 저장


다음은 Documents Directory에 파일을 저장하는 방법이다.


// 경로 

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"test.txt"];


// 컨텐츠

    NSString *content = @"테스트로 저장해요.";

    NSError *err = NULL;

    

 // 파일 저장

    [content writeToFile:filePath

              atomically:NO

                encoding:NSUTF8StringEncoding

                   error:&err];





2. 파일 읽기


파일을 읽을 때에는 이렇게 읽는다.


// 경로 

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"test.txt"];


// 컨텐츠 읽기 

    NSString *content = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error: NULL];





3. 파일명 변경


파일명을 변경하려면 다음과 같이 변경할 수 있다.



// 경로 

    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *documentsDirectory = [paths objectAtIndex:0];


// 해당 파일 경로

    NSString *oldPath = [documentsDirectory stringByAppendingPathComponent:@"test.txt"];

    NSString *newPath = [documentsDirectory stringByAppendingPathComponent:@"renamed.txt"];

    

// 파일명 변경

 NSError *err = NULL;

    [fileManager moveItemAtPath:oldPath toPath:newPath error:&err];




4. 파일 삭제

특정 파일을 지울 때는 다음과 같이 경로를 지정하여 삭제할 수 있다.

// 경로 

    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *documentsDirectory = [paths objectAtIndex:0];


// 파일 삭제

[fileManager removeItemAtPath:[documentsDirectory stringByAppendingPathComponent:@"renamed.txt"] error:NULL];





네트워크 통신하다 보면 서버 시스템이 UTF-8로 데이터를 넘기지 않는 경우가 있다.


서버에서 EUC-KR로 데이터를 넘겨주면 이를 그냥 NSString으로 넘기면 글씨가 깨지기 마련이다.


다음과 같이 인코딩을 지정하여 const char 로 변환 후 NSString으로 데이터를 생성하면 정상적으로 텍스트를 읽을 수 있다.



NSUInteger encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_KR);

const char * eucKRString = [original cStringUsingEncoding:encoding];

NSString *encodedString = [NSString stringWithUTF8String:eucKRString];



혹은 다음과 같이 사용해도 된다. 

NSUInteger encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_KR);

NSString * encodedString = [NSString stringWithCString:[data bytes] encoding:encoding];



출처 - http://stackoverflow.com/questions/13883638/how-to-encode-nsstring-to-euc-kr

+ Recent posts