글 입력/수정 중 페이지 이동/새로고침/닫기시 확인 메시지 띄우기

웹에서 글(게시판, 이메일 등)을 작성중에 잘못 클릭해서 작성중인 글이 사라지면 곤란하다.

물론 의도한 경우 나가는건 상관 없겠지만, 의도하지 않았는데 나가지거나 이동되어 작성중인 내용이 사라진다면… 끔찍하다.

사용자의 노력이 허망하게 날라가는것을 방지하기 위해 최근에는 사용자의 노력이 날라갈 수 있는 경우(정의하기 나름)에 확인 메시지를 보여주고, 확인한 경우에만 페이지 이동등을 해준다. 마지막 안전장치인것이다.

그럼 우리가 만드는 웹에서도 이런 안전장치를 설치하자.

위의 코드는 페이지 리프레쉬, 페이지 이동이 발생하는 경우만 확인 메시지를 띄어준다. 페이지 이동 없이 다른 메뉴로 이동이 가능한 angularjs의 경우엔 페이지 이동 이벤트에 대해서 처리를 해줘야한다. 아래 코드는 ui-router 를 사용하는 경우에 동작한다.

자 안전장치를 설치해서 사용자에게 더 나은 경험을 제공하자!

(번역) angularjs 마법 이해하기: 기본 데이터 타입(primitive type)을 바인딩 하지 마라

원문: http://www.codelord.net/2014/05/10/understanding-angulars-magic-dont-bind-to-primitives/

angularjs를 사용하고 있다면 ‘기본 데이터 타입을 바인딩 하지 마라’라는 말을 들어본적이 있을 것이다. 이 글에서는 기본 데이터 타입을 바인딩하면 발생할 수 있는 문제를 예제를 통해 설명하겠다.

예제

책의 태그정보를 다루는 앱이 있다고 가정하자. 사용자가 책의 태그 정보를 수정할 수 있는 앱을 angularjs로 만들면 다음과 같다.

위 코드를 직접 확인해보자. input의 책 태그 정보를 수정해보자. input의 값은 문제없이 변경이 된다. 하지만 book.tags에 반영이 되진 않는다.

이 현상은 ng-repeat이 각 tag에 대해서 별도의 childe-scope을 만들기 때문에 input에서 변경한 값이 book.tags에 반영이 되지 않기 때문에 발생한다. 코드로 표현하자면 다음과 같다.

bookCtrl scope = { tags: [ 'foo', 'bar', 'baz' ] } ng-repeat child scopes = { tag: 'foo' }, { tag: 'bar' }, { tag: 'baz' }

child scope을 보면 book.tags 값과 양방향 데이터 바인딩 처리를 해주고 있지 않다. 그래서 child scope의 데이터를 담고 있는 input의 값을 변경해도book.tags의 값은 변하지 않는다.

양방향 바인딩이 되려면 $watch, $watchCollection 등의 처리를 해줘야 한다. (자세한건 여길 참고)

tag의 값을 기본형 데이터가 아닌 객체를 사용해 저장하면 위와 같은 문제는 해결이 된다. 하지만 book.tags의 데이터 타입을 변경하고 싶지 않을때는 어떻게 해야 할까?

잘못된 시도

다음과 같이 시도해볼수 있다.

ng-model이 child scope의 값 대신 book.tags를 바로 연결한다. 이제 input의 변경이 book.tags의 값에 반영된다. 데모를 확인해보자. 이게 뭔가 싶을거다. ㅋㅋ

ng-repeat이 이런 현상을 발생시키는 범인이다. ng-repeat은 리스트의 모든 값들을 감시하고 있다가 값이 변경되면 변경된 값을 해당 엘리먼트에 반영한다.

자바스크립트의 기본 데이터 타입은 불변(immutable)의 값이다. 값을 변경한다는 것은 실제로 가지고 있는 기존의 데이터를 버리고 새롭게 변경된 값을 갖는 객체를 생성해서 사용하는 것이다. 이러한 기본 데이터 타입의 변경은 ng-repeat이 기존 데이터를 바인딩하고 있던 input을 삭제하고 새롭게 생성된 데이터를 바인딩하는 input을 추가하는 것이다. 이게 input에서 값을 수정할때마다 포커싱을 잃는 이유다.

해결 방법

ng-repeat이 기본 데이터 타입 값을 감시하는게 아니라 리스트의 index를 이용하도록 하는 것이다. 어떻게하면 ng-repeat가 index를 감시 대상으로 설정할 수 있을까? 운좋게 angularjs 1.2 버전에서 도입된 track by 를 이용하면 된다.

결과를 보자

track by성능 향상에 더 유용하지만, 이러한 형태로 사용이 가능한걸 알고 있는것도 좋다. 이런 내부 동작을 이해하면 angularjs 마법을 이해하는데 도움이 된다.

window mysql 에서 케릭터셋 설정

참조: http://jmnote.com/wiki/MySQL_%EC%BA%90%EB%A6%AD%ED%84%B0%EC%85%8B_utf8_%EC%84%A4%EC%A0%95
MySQL을 사용하다 보면 인코딩 문제가 발생할 수 있다. 이럴 경우 mysql에 접속해서 다음 쿼리를 날리면 현재 설정값을 받아올 수 있다.

show variables like ‘char%';
mysql> show variables like ‘char%';
+————————–+—————————-+
| Variable_name | Value |
+————————–+—————————-+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+————————–+—————————-+

latin으로 되어 있는 항목을 utf8으로 설정을 바꿔준다.
my.cnf 또는 my.ini 파일에 다음 설정 값을 넣어준다.

[mysqld]

### 2012-07-03 utf8 setting
init_connect=SET collation_connection = utf8_general_ci
init_connect=SET NAMES utf8
character-set-server=utf8
collation-server=utf8_general_ci

[client]
default-character-set=utf8

이렇게 설정하고 나서 mysql 서버를 재시작 해준다.

다시 mysql의 케릭터셋 설정 정보를 확인한다.

show variables like ‘char%';
mysql> show variables like ‘char%';
+————————–+—————————-+
| Variable_name         | Value                          |
+————————–+—————————-+
| character_set_client | utf8                             |
| character_set_connection | utf8                    |
| character_set_database | latin1                    |
| character_set_filesystem | binary                  |
| character_set_results | utf8                           |
| character_set_server  | latin1                        |
| character_set_system | utf8                          |
| character_sets_dir      | /usr/share/mysql/charsets/ |
+————————–+—————————-+

위와 같이 설정이 변경된걸 확인 했다면 이제 utf8으로 동작할 것이다. 즐프~

drop all table in mysql

직접 서버를 운영하는게 아니라 호스팅 업체를 이용하면 제약이 많다.

최근 카페 24 호스팅에서 작업을 하는데, mysql 권한이 없어서 간단한 작업 이지만 매우 번거로웠던 적이 있다.

그런 작업중에, 특정 데이터베이스 내의 모든 테이블을 지워야 할 경우에, 권한이 있으면 데이터베이스를 날리고 새로 만들면 되지만, 권한이 없어 그러지 못했다.

그럴 경우 다음 명령을 실행하면 한방에 처리가 끝난다.

참고: http://www.thingy-ma-jig.co.uk/blog/10-10-2006/mysql-drop-all-tables

apache2 + php 5 + oci 8 설정

참고: http://jianmingli.com/wp/?p=1263

옵션 정보

#apache
./configure \
–enable-so \
–enable-rewrite=shared \
–enable-headers \
–enable-proxy \
–enable-proxy-balancer \
–enable-proxy-connect \
–enable-proxy-http \
–enable-rewrite \
–prefix=/app/apache2

#php
./configure \
–with-apxs2=/app/apache2/bin/apxs \
–enable-bcmath \
–with-pear \
–enable-sockets \
–with-zlib \
–with-gd \
–with-freetype \
–with-oci8=shared,instantclient,/usr/lib/oracle/11.2/client64/lib/

 

이후 php.ini 에 extension=oci8.so 를 추가해주면 된다.

NAS(synology)에서 crontab 사용하기

주기적으로 무언가를 해야한다면, 흔히 생각할 수 있는 것이 crontab이다. 물론 서버가 리눅스일 경우에 ^^

지금 NAS를 사용하고 있는데, NAS가 리눅스 기반이기 때문에 당연히 crontab이 있을 것이라고 생각했다. 리눅스에서와는 조금 다르지만, 역시나 존재했다.

crontab에서 동작할 무언가를 등록해야 한다. 리눅스에서는 ‘crontab -e’ 와 동일한 작업은 다음을 이용한다.

vi /etc/crontab

이렇게 하고 나면 이제 리눅스에서 쓰던 그대로 등록을 한다.

NAS에서는 등록 후에 cron 데몬을 리스타트 해야 적용된다!

synoservice –restart crond

여기까지 했으면 이제 등록 끗!

덧, 제대로 돌고있는지 확인하기 위해 로그를 남겨야할 경우가 있다. 구글링을 해보니 ‘/etc/syslog.conf’에 남는다고 했는데, 나한테 해당되는 내용이 아니었다.
그래서 자동으로 남기는게 아니라, 내가 원하는 위치에 원하는 파일로 남기도록 처리했다. 방법은 간단하다. crontab에 등록할 때 표준 출력을 내가 원하는 위치로 등록하면 된다.

예) /usr/bin/php /var/services/web/curl.php >> /var/services/web/cron.log

String.intern()은 뭘 하는거지?

String.intern에 설명에 앞서 다음 퀴즈를 한번 보자.

 

전제조건) String string = “test”;

전제조건) String test = “test”;

Q1) test == “test”  ?

Q2) test == new String(“test”) ?

위 문제의 답(java 1.6+에서)은 각각 true, false 이다.

 

왜 그럴까?

위 문제의 답을 알기 위해서는 intern이 뭘 하는지에 대해서 알아야 한다.

사실 위와 같은 답이 나온 이유는 일단 컴파일러가 점점 똑똑해져서 사용자가 의도를 해하지 않는 범위 내에서 최적화 작업을 하기 때문입니다. 위 코드에서 컴파일러가 최적화를 위해 해주는 작업은 Q1의 “test”를 new String(“test”).intern()으로 바꾸어 주는 것입니다. 더 정확히 표현하면 코드상에서 스트링 익스프레션으로 선언된 스트링을 자동으로 String.intern으로 바꾸어 주는 것입니다.

그럼 왜 String.intern()이 어떤 일을 하길래 최적화라는 명목 아래 알아서 변환 작업을 해주는 걸까?

JVM에는 String을 저장해두는 String pool 이라는 것이 있는데, 이 녀석은 String이 너무 많이 로딩될 경우 OME가 발생할 여지가 있기 때문에 동일한 값(char 배열)을 갖는 경우에는 String pool에서 받아오게 한다. 이렇게 해서 같은 값을 갖고 있지만 다른 메모리를 차지하는 String을 줄여서 메모리를 보다 효율적으로 사용하게 된다. (intern으로 선언할 경우 String pool에 캐쉬되지만 이를 직접 참조하는 값이 없을 경우에는 gc된다.)

자 그럼  왜 Q1은 true를 리턴하는지 설명이 가능하다. “test”로 선언할 경우 새로운 메모리를 할당해서 생성될거라 예상 하지만 intern을 통해서 String pool에 캐쉬되어 있는 String을 리턴한다. 그렇기 때문에 동일한 주소값을 갖는 객체를 비교하기 때문에 true를 리턴한다. 역으로 Q2에서는 new를 통해 새로운 메모리를 할당해서 생성되기 때문에 비교 값이 false를 리턴하게 된다.

개인적으로는 메모리 사용이 정말 중요한 embeded 프로그램 등에서는 intern의 사용이 고려될 수 있겠지만, 웹 애플리케이션 등을 개발할 경우에는 고려할 필요가 없다고 생각한다.

미비한 성능향상 보다는 가독성 높은 코드가 더 값지다고 생각하기 때문이다.

 

Play framework에서 CRUD class가 보이지 않을 경우

http://www.playframework.org/documentation/1.2.4/crud

 

이클립스 기반으로 위의 튜토리얼을 따라하다 보면 CRUD class 가 보이지 않는 경우가 있다.

이 건, 이클립스에서 사용하는 내부 캐쉬 문제라고 한다.

이 경우에는 다음과 같은 순서로 해결 가능하다.

 

1. 이클립스 상에서 해당 프로젝트를 삭제(컨텐츠까지 지우면 안됨).
2. play eclipsify app-name
3. 이클립스에서 다시 import

 

이거 몰라서 한참 고생했네~