Categories
Swift 개발(Development)

FetchedRequest.wrappedValue 접근시 crash 문제

하고자 했던 것은 앱이 실행될 때, CoreData 에 데이터가 있는지 체크해서 있으면 그것을 사용하고, 없으면 인터넷에서 데이터를 받아 그것을 CoreData 에 저장하려는 것이었다. 체크를 위해 처음에 작성한 코드는 아래와 같다.

struct ContentView: View {
    let req = FetchRequest<UserModel>(entity: UserModel.entity(), sortDescriptors: [])
    @State private var needToFetch = false

    init() {
        checkData()
    }

    var body: some View {
        ... // use needToFetch variable
    }

    func checkData() {
        if self.req.wrappedValue.count == 0 {
            print("no data")
            self.needToFetch = true
        } else {
            print("there is data")
            self.needToFetch = false
        }
    }
}

그런데 실행해보면 self.req.wrappedValue.count 에 접근하는 순간 EXC_BAD_INSTRUCTION 에러와 함께 crash가 발생한다. checkData 함수 안을 DispatchQueue.main.async 로 감싸보면 crash 가 발생하지 않는데, 이것을 보면 thread 와 관련된 이슈가 있는 것 같다. 대신 async 로 처리하게 되면 @State 가 무력화 되는지 needToFetch 의 값이 바뀌어도 body가 다시 그려지지 않는다.

그래서 해결한 방법이 아래와 같다.

struct ContentView: View {
    let req = FetchRequest<UserModel>(entity: UserModel.entity(), sortDescriptors: [])
    @State private var needToFetch = false

    var body: some View {
        Text("hello world")
            .onApper(perform: req.wrappedValue.count == 0 ? fetchData : useCoreData)
    }
}

init 함수를 통해 fetchRequest.wrappedValue 에 접근하지 않고 onApper modifier 를 통해 분기하니 crash 없이 원하는 결과를 얻을 수 있었다.

Categories
Javascript + jQuery 개발(Development)

JavaScript, jQuery 공부에 유용한 사이트들

회사 신입에게 JavaScript와 jQuery를 좀 알려줘야 할 일이 생겼다. 1:1로 붙어서 알려주면 좋겠지만 회사에서는 그러기 힘드니, 사이트를 몇 개 던져주고 공부시켰다(…). 궁금한 거 있으면 질문하라고 하며 틈틈히 체크해보는 방식으로 했는데, 꽤나 도움이 된 것 같아 공유차원에서 정리해보는 글.

Code School – JavaScript Path (영어)

CodeSchool - Javascript Path
CodeSchool – Javascript Path

기본적으로 많은 도움이 된 사이트가 바로 이 Code School이다. 위에 링크한 JavaScript Path에서는 자바스크립트 전문가가 되기까지 도움이 되는 지식들이 단계별로 구성되어 있다. JavaScript Road Trip Part 1과 Try jQuery는 무료로 체험해 볼 수 있고, 이후 과정은 등록(월 US $29) 이후 수강이 가능하다. 단계를 넘어갈 때마다 포인트가 누적되기 때문에 게임을 하는 기분도 들고 입력에 따라 인터랙티브한 피드백을 계속 받을 수 있기 때문에 배우는 과정도 재미있다. 무언가 새로운 언어를 배우고 싶다면 이곳을 강추한다. JavaScript 외에도 Ruby, iOS 등 다양한 과정이 있다. (전체 코스 보러가기)
* jQuery는 try.jquery.com 으로도 접속할 수 있다.

Learning Advanced JavaScript (영어)

John Resig의 Learning Advanced JavaScript
John Resig의 Learning Advanced JavaScript

jQuery의 창시자인 John Resig이 자신의 저서 Secrets of the JavaScript Ninja를 위해 공개한 예제 사이트. 조금 더 난이도가 있는 JavaScript 기법들을 익힐 수 있다. 책 없이 하려니 주석과 예제를 통해 내용을 파악해야 하지만, 그래도 인터랙티브한 환경을 제공하기 때문에 이것저것 즉석에서 확인해 볼 수 있는 점이 장점.

JSFiddle

JSFiddle
JSFiddle

간단하게 HTML, CSS, JavaScript 코드를 넣고 결과물을 확인할 수 있는 사이트. 한 눈에 모든 것을 볼 수 있어서 테스트가 필요할 때면 틈틈히 사용하고 있다. jQuery 등의 플러그인을 참조하여 사용할 수도 있어 유용하다. 결과물을 Save 한 경우 별도의 URL이 생성되는데, 이를 통해 중간 결과물들을 다른 사람들과 공유할 수도 있게 되어 있어 유용하다. 비슷하게는 JS Bin이란 곳도 있는 것 같다.


여기까지가 신입에게 알려준 사이트들. 한국어 사이트가 없다(…). 아무래도 인터랙티브하게 익힐 수 있는 곳을 찾다보니 위의 곳들을 알려주게 되었다. 한국어로 된 사이트 중 알고 있는 곳은 생활코딩이 유일한데, 인터랙티브한 환경을 제공하지는 않지만 한국어라는 점과 동영상 강의가 제공되니 듣기 편하다는 장점이 있다.
위의 사이트들을 보면, 이제는 뭔가 배우려고 할 때 더 재밌게 배울 수 있는 방법이 늘어나고 있다는 걸 느낄 수가 있었다. 가만히 주입식으로 배우기보다는 직접 만지고 따라해보면서 게임처럼 익힐 수 있는 곳들. 앞으로 이런 시장이 좀 더 커져서 원하는 것을 재미있게 배울 수 있는 길이 더 많아졌으면 좋겠다.
+ 2013/12/12 추가
한군데 더 생각나서 추가한다. jsPerf 라는 곳인데, JavaScript 코드들의 성능을 비교해볼 수 있는 사이트이다. 예시로 jQuery selector와 표준 selector를 비교한 페이지를 링크해둔다.

Categories
개발(Development)

[.NET] 2-way SSL 환경에서 WebService 호출하기

2-way SSL이란, 일반적으로 Server 쪽 인증서만으로 SSL 연결을 하는 것과는 달리, Client 쪽에서도 인증서를 보내 서로 인증을 하는 환경을 말한다. 때문에 서로 데이터를 주고 받기 위해서는 Server 인증서, Client 인증서 두 개가 각각 필요하다.

2-way SSL
2-way SSL (출처: codeproject)

이 글에서는 일단 Client 인증서를 사용해서 Server 쪽에 Web Service API를 호출하는 것까지, 사용했던 방식을 정리해보고자 한다.

1. 인증서 설정

우선 Client 인증서가 필요하고, 해당 인증서를 Server 쪽에 등록하여 인증에 사용할 수 있도록 구성해야한다. 이 부분은 Server쪽 업체쪽에서 Client 인증서도 만들어 보내줬기 때문에 자세한 설명은 힘들다. 어쨌든 Client 인증서를 컴퓨터에 설치한 뒤, 브라우저를 열어 해당 서버에 잘 접속이 되는지를 확인해본다. 아마도 접속시 어떤 인증서를 사용할 것인지 물어보는 확인창이 열릴 것이고, 인증서 선택 후 페이지가 제대로 나오면 인증서에는 문제가 없는 것이다.
이제부터는 API 에서 사용하기 위해 설정하는 방법이다.

  1. [시작]-[실행]-mmc 를 입력하여 Microsoft Management Console 실행
  2. [파일]-[스냅인 추가/제거] 선택
  3. [인증서]를 선택하고 [추가] 버튼을 누른 뒤, [컴퓨터 계정]-[로컬 컴퓨터]를 선택한다.
    mmc_certificates
  4. [개인용]에서 오른쪽 클릭 후 [모든 작업]-[가져오기] 선택
  5. 찾아보기를 누른 후 파일형식을 [개인정보교환(*.pfx, *.p12)]로 바꾼 뒤, 위에서 설치했던 인증서 경로를 지정한다. (필자의 경우 client 인증에 사용되는 인증서 형식이 p12였다)
  6. 비밀번호를 물어본다면 입력해주고, 기본 설정대로 [다음]을 눌러 [마침]이 나올 때까지 완료해 준다.
  7. 인증서가 추가되었다면, 이제 IIS에서 접근할 수 있도록 권한을 열어주어야 한다. 추가된 인증서를 오른쪽 클릭하고 [모든 작업]-[개인 키 관리]를 선택한다.
  8. [그룹 또는 사용자 이름]에서 [추가]를 클릭
  9. 찾을 위치에서 범위를 컴퓨터 전체로 지정하고, 선택할 개체 이름에는 “IIS AppPool\DefaultAppPool”을 넣어 추가해준다. 이름 확인을 눌러주었을 때 DefaultAppPool 로 바뀐다면 제대로 된 것이다.(*IIS에서 인증서가 필요한 응용 프로그램 풀 이름을 확인하자. 여기서는 DefaultAppPool에 서비스가 올라가 있었다.)
  10. 이로써 MMC에서 설정은 끝났다.

2. 2-way SSL 환경에서 서비스 중인 WebService의 WSDL 등록

서버쪽 업체에서는 .NET 2.0 SOAP Web Service를 통해 API를 제공하고 있었다. 웹 참조 추가를 통해 WSDL을 등록해주면 해당 서비스가 제공하는 API를 이용할 수 있다. 일반적인 웹 참조 추가는 아래와 같은 순서로 이루어진다.

  1. 솔루션 탐색기의 [참조]에서 오른쪽 클릭하여 [서비스 참조 추가]를 선택(VS2010 기준)
  2. 왼쪽 아래의 [고급] 버튼을 누르고, 다음 화면에서 [웹 참조 추가]를 누른다.
  3. URL에 WebService의 주소를 넣어주면, 자동으로 ‘이 URL에서 찾은 웹 서비스’에 해당 서비스 이름이 노출된다.
  4. 사용하려는 이름을 지정하고 [참조 추가]를 누른다.

그런데 2-way SSL 환경에서 서비스 중인 URL을 입력하였더니, 웹 서비스의 metadata를 불러올 수 없는 오류가 발생하였다.

‘https://xxx.xxx.com/aaa.asmx’을(를) 다운로드하는 동안 오류가 발생했습니다.
HTTP 상태 403: Forbidden(으)로 인해 요청하지 못했습니다.
‘https://xxx.xxx.com/aaa.asmx/$metadata’을(를) 다운로드하는 동안 오류가 발생했습니다.
요청이 실패했습니다(HTTP 상태 403).

이때 아래와 같이 해결할 수 있다.
– 해결방법 1 – WSDL 로컬에 저장하여 참조하기

  1. 브라우저에서 웹 서비스의 WSDL 주소로 접근하여 정보를 확인한다.
  2. [파일]-[다른 이름으로 저장]을 통해 wsdl 파일을 로컬에 저장한다.
  3. VS에서 웹 참조 추가를 통해 URL을 입력하는 화면까지 진행한다.
  4. URL 입력 창에 아래처럼 경로를 넣어준다.
    file://c:\temp\aaa.wsdl
  5. 서비스 이름을 확인한 뒤, 웹 참조 이름을 지정하고 [참조 추가]를 누른다.

– 해결방법 2 – svcutil.exe 사용하기
해결방법 1로 로컬에서는 문제 없이 서비스호출이 됐지만, 실제 서비스를 올리려고 하면 XmlSerializer 참조 오류가 발생했다. 결국 웹 참조 대신 wsdl -> class 변환 툴을 활용하여 처리하는 방식으로 변경하였다.

  1. Visual Studio 명령 프롬프트 실행(글 작성시는 VS2010 환경)
  2. svcutil.exe <wsdl 파일 경로> /language:<사용할 언어>
    ex) svcutil c:\temp\aaa.wsdl /language:C#
  3. 성공적으로 수행되면 클래스 파일과 output.config 파일이 생성된다.
  4. 클래스 파일을 프로젝트에 추가하고, output.config 파일의 내용은 web.config 파일에 추가하여 준다.
    output.config 에는 웹 서비스 호출에 사용될 endpoint 주소 등이 들어가 있다.
  5. 4에서 붙여넣은 내용 중에, <security> 항목 아래에 <transport clientCredentialType=”None”> 이란 항목이 있다. Client 인증서를 사용할 것이기 때문에, “None”을 “Certificate”로 바꿔준다.

3. 인증서와 함께 서비스 호출

이제는 Client 인증서와 함께 서비스를 호출하는 부분이다. 이 부분은 소스에서 몇 줄만 추가해주면 된다.
– 해결방법 1의 경우

// 웹서비스 객체 생성
MyWebService.Service1 s1 = new MyWebService.Service1();
// 로컬 컴퓨터의 인증서 보관함에 접근한다. 위에서 인증서를 로컬 컴퓨터에 저장했음을 기억하자.
X509Store localStore = new X509Store(StoreLocation.LocalMachine);
localStore.Open(OpenFlags.ReadOnly);
// 로컬 보관함에서 사용할 인증서를 찾는다. 여기서는 이름을 사용했다.
X509Certificate2Collection certs = localStore.Certificates.Find(X509FindType.FindBySubjectName, "MyClientCertName", true);
if( certs.Count > 0 ) {
    X509Certificate2 cert = certs[0];
    s1.ClientCertificates.Add(cert); // 인증서를 웹서비스 객체의 Client 인증서에 추가한다.
}
// 서비스 호출
s1.method1(param1, param2);

– 해결방법 2의 경우

// 웹서비스 객체 생성
MyWebService.Service1 s1 = new MyWebService.Service1();
// 사용할 인증서를 찾아서 객체에 추가해준다.
s1.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyClientCertName");
// 서비스 호출
s1.method1(param1, param2);

이제 실제로 프로그램을 실행해 서비스 호출 결과가 잘 오는지 확인하면 된다.

Categories
Javascript + jQuery 개발(Development)

placeholder 미지원 브라우저 대응

낮은 버전의 IE처럼 HTML5에 추가된 placeholder 속성을 지원하지 않는 브라우저가 있다.
그런 경우 javascript를 이용하여 placeholder 속성을 지원하는 것처럼 흉내내는 방법.
focus를 잃었을 때 placeholder 속성의 값을 value로 넣어주고, focus를 얻어주면 빈값을 넣어주는 방식이다.
이 방법의 단점은 Form을 submit 할 때 input 값에 placeholder 속성값이 들어가 있는지 확인을 해주어야 한다는 점이다. (아래 추가된 내용으로 해결)
또 script 내에서 input 값을 빈값으로 변경할 경우(ex. $(“#input1”).val(“”);), 다시 placeholder를 노출시키기 위해서는 blur() 함수를 따로 호출해야 한다.(focus 변화가 없으므로) (아래 추가된 내용으로 해결)
HTML

Javascript

// placeholder를 지원하지 않는 브라우저 대응
function initPlaceholder() {
	if (!("placeholder" in document.createElement("input"))) {	// 지원여부 체크
		var $input_placeholder = $("input[placeholder]");
		$input_placeholder.each(function () {
			var value = $(this).val().trim();
			$(this).focus(function () {
				if ($(this).val().trim() == $(this).attr("placeholder")) {
					$(this).css("color", "");
					$(this).val("");
				}
			}).blur(function () {
				if ($(this).val().trim() == "") {
					$(this).css("color", "#a6a6a6");
					$(this).val($(this).attr("placeholder"));
				} else {
					$(this).css("color", "");
				}
			}).blur();
		});
	}
}

+ 내용 추가
jQuery의 val function을 오버라이딩해주면 값 변경시마다 blur를 호출할 필요가 없다(값을 val 함수로 변경한다는 전제조건 하에서. input1.value = “”; 는 적용 안됨).
+ 내용 추가
val function 오버라이딩 부분 중 getter 부분도 placeholder 값과 비교하여 리턴하도록 변경.
한계점은 $(“#input1”).val(); 시에는 원하는대로 동작하지만 document.getElementById(“input1”).value 처럼 접근할 때에는 placeholder 값이 나온다는 것.
기존에는 focus 함수에서 placeholder 값과 비교했는데, val() getter를 오버라이드 했기 때문에 “” 와 비교하는 점에 주의.

function initPlaceholder() {
	if (!("placeholder" in document.createElement("input"))) {	// Browser 지원여부 체크
		var $input_placeholder = $("input[placeholder]");
		$input_placeholder.each(function () {
			$(this).focus(function () {
				if ($(this).val().trim() == "") {
					$(this).css("color", "").val("");
				}
			}).blur(function () {
				if ($(this).val().trim() == "") {
					$(this).css("color", "#a6a6a6").val($(this).attr("placeholder"));
				} else {
					$(this).css("color", "");
				}
			}).blur();
		});
		// jQuery val function overriding for placeholder
		(function ($) {
			var org_val = $.fn.val;
			$.fn.val = function (value) {
				var placeholder = $(this).attr("placeholder");
				if (typeof value != "undefined") { // setter
					if ((value == "" || value == placeholder) && $(this).is("input[placeholder]:not(:focus)")) {
						$(this).css("color", "#a6a6a6");
						value = placeholder;
					} else {
						$(this).css("color", "");
					}
					return org_val.call(this, value);
				}
				else { // getter
					if( this[0].value == placeholder ) {
			                        this[0].value = "";
    			                }
					return org_val.call(this);
		                }
			};
		})(jQuery);
	}
}

Gist Github에서 update 시키고 있다.

Categories
개발(Development)

[SQL] Select 결과를 ids 순으로 정렬하기

SELECT * FROM tbl
WHERE id IN ( 5, 10, 1, 9)

위와 같은 SQL문은 특정 아이디에 해당 되는 값들을 추출할 때 자주 사용하는 구문이다. 하지만 보통 위와 같은 쿼리를 수행시킬 경우, 결과값이 1, 5, 9, 10 처럼 id를 기준으로 정렬되게 된다.
아래는 이를 피하고 IN 안에 넣은 순서대로 데이터를 추출하고 싶을 때 사용할 수 있는 방법이다.(아래 문법은 MySQL에서 사용한 문법이다)

SELECT * FROM tbl
WHERE id IN (5, 10, 1, 9)
ORDER BY INSTR( ‘5,10,1,9’,  id)

허무할 정도로 별 거 없다. IN 안에 넣은 ids 문자열에서 id가 위치하는 index 값을 추출(INSTR 함수)하여 그것을 기준으로 정렬하는 방법이다.
출처: SELECT In order by specific Ids – Microsoft SQL Server에 달린 댓글 중에서
다른 좋은 방법을 알고 있는 분이 있다면 공유 주시길… 🙂

Categories
개발(Development)

[Facebook SDK] feed 함수 호출시 picture에 넣은 이미지가 나오지 않는 문제

Facebook SDK에 feed란 함수가 있는데, 앱을 이용해 사용자의 타임라인에 글을 작성하는 함수다.

feed 함수로 글을 작성한 결과 화면

이때 본문 내용 왼쪽에 나타나는 이미지를 지정할 수가 있는데, 이 역할을 하는 것이 picture라는 파라미터다. picture에 값을 넣으면, app_full_proxy.php 라는 녀석이 Facebook에서 호출되면서, 해당 이미지를 표시해준다.
그런데 이미지가 통 안 나오는 경우가 있다. 며칠을 삽질 했는데, 늘 그렇듯이 결론은 쉽다.
feed 함수의 picture 파라미터에 대한 설명. 아래 source에 주목!

Feed 함수의 developer 문서를 살펴보면, picture 아래에 source 파라미터에 대한 설명이 있다. source와 picture가 함께 지정되면, picture를 무시하고 source만을 사용한다는 무시무시한 설명이…
결론.
이미지를 넣고 싶으면, source 파라미터는 사용하지 말고 picture만 사용해야 한다.

Categories
Javascript + jQuery

[Javascript] 팝업 차단 피하기

User action이 있을 때 popup을 띄우는 것은 허용된다. ex) click 등
ajax 처리를 하는 경우에도 callback 함수 바깥에서 띄우면 된다.
안 되는 경우

// 클릭 이벤트 핸들러
function didClick() {
	$.ajax({
		url: "someurl.php",
		success: function() {
			window.open("popup.html"); // blocked
		}
	});
}

해결 방안

// 클릭 이벤트 핸들러
function didClick() {
	var result = false;
	$.ajax({
		url: "someurl.php",
		success: function() {
			result = true;
		}
	});
	if( result ) {
		window.open("popup.html"); // it will works
	}
}
Categories
개발(Development)

[Eclipse] jcraft SFTP export 오류

상황 설명

SFTP로 A라는 서버에 업로드를 했다가 B라는 서버로 대상을 바꿔 업로드를 한 다음, 다시 A 서버로 업로드가 안 되는 문제

오류 메시지

xxx is already mapped to com.jcraft.eclipse.team.sftp.SFTPDeploymentProvider

해결 방안

<pathToEclipseWorkspace>/.metadata/.plugins/org.eclipse.core.resources/.projects/<projectName>/org.eclipse.team.core/.deployments

위의 경로에 해당하는 파일을 연 다음, <provider>에 해당하는 녀석을 지우고 이클립스 재시작

출처

http://stackoverflow.com/questions/3823235/how-to-edit-ftp-accounts-in-jcraft-sftp-plugin-for-eclipse

Categories
HTML + CSS

긴 텍스트 줄이기(HTML과 CSS 이용)

텍스트 길이가 폭 보다 긴 경우 뒷 내용을 줄이면서 뒤에 …을 붙이는 방법
HTML

some long texts

CSS

.wrapper { white-space: nowrap; }
.ellipsis {
	overflow: hidden;
	text-overflow: ellipsis;
}
Categories
개발(Development)

모바일 WPtouch에서 Skip to comments 링크를 Disqus로 연결하기

뭔가 굉장히 어려운 제목이다. 상황은 이렇다. WordPress 블로그를 모바일 환경에 적합하게 보여주는 WPtouch 라는 플러그인이 있다. 아이폰에서 보여지는 화면이 대략 아래와 같다.

제목 아래에 보면 “↓ Skip to comments”라는 링크가 있다. 해당 링크를 누르면 본문을 건너띄고 바로 댓글을 달 수 있는 곳으로 화면을 이동시켜준다. WordPress에서 제공하는 댓글 시스템을 그대로 이용하고 있다면 아무런 문제가 없을 것이다. 하지만 Disqus라는 소셜댓글시스템을 블로그에 설치했다면 위 링크가 동작하지 않는 것을 알 수 있다. 매번 수동으로 스크롤을 내려서 이동해야 하기 때문에 사람에 따라 불편할 수 있는 부분이다. 그래서 해당 부분을 수정하기로 했다.
수정을 위해서는 WPtouch 플러그인의 테마(Theme) 폴더로 접근을 해야한다. WordPress의 플러그인 에디터에서는 테마 폴더가 보이지 않아서 FTP 프로그램을 이용해 접근했다. 보통 Default 테마를 사용하기 때문에 해당 위치로 이동하여 포스트 영역을 담당하는 Single.php 파일을 연다.

해당 파일을 살펴보면 skip to comment 링크를 추가하는 소스를 찾을 수 있다. 해당 부분을 다음과 같이 변경해준다.

원본 소스. 현재 #dsq_add_new_comment 로 링크가 걸려있다.

수정한 소스. #disqus_thread 로 링크를 변경해준다.

이 문제는 Disqus의 HTML이 dsq_add_new_comment을 사용하다가 disqus_thread로 변경됐기 때문에 생기는 문제 같다(추측임). WPtouch 개발자가 수정해주기 전까지는 위와 같은 방법으로 조치를 취해줘야 할 것 같다.