equals()를 레퍼런스 비교하는데 사용 하지 마세요.

회사에서 다른 분의 코드를 보다가 다음 코드가 있어서 질문 했습니다. (해당 소스를 그대로 가지고 온것은 아닙니다.)

레퍼런스 비교하는데 썻다고 합니다. 그래서 조금 더 찾아봤습니다. equals는 모든 클래스가 암묵적으로 확장하는 Object의 메소드 입니다. 그래서 오버라이딩을 하지 않으면 Object에 정의된 equals()를 사용합니다. 그 구현을 보니 다음과 같았습니다.

결국엔 객체간의 레퍼런스 비교하는 ‘==’를 사용하고 있습니다. 그럼 equals는 왜 존재하는 걸까? 단순히 레퍼런스 비교로 동일 여부를 판단하기 어려운 경우 각 객체에서 동일성 판단 메소드를 구현해서 사용하라는 겁니다. 그러니 equals로 동일 여부를 판단하게 되면 해당 구현을 읽는 사람은 equals가 오버라이드 되었는지 확인을 해야 합니다. 그런데 오버라이드 안된 경우라면 결국엔 위의 Object.equlas를 사용하게 되는 것이죠. 그러므로 레퍼런스 비교를 위해 equals를 사용할 경우 다른 사람이 봐서 직관적으로 이해하기 어렵습니다. 또 오버헤드는 적지만 불필요하게 메소드를 한번 타고 들어갑니다.

객체간의 레퍼런스 비교는 ‘==’를 사용하는 것이 훨씬 더 직관적입니다.

Spring-Data-JPA 사용하기

참고: http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/
User라는 도메인 관련해서 RDB에 CRUD를 작성하는 걸 목표로 한다.
1. Domain 정의

  • @Entity: 해당 도메인 객체를 엔티티로 사용한다는 것을 알려준다.
  • @Id: 엔티티의 Id 를 정의해준다. Id는 반드시 있어야 한다.
  • @GeneratedValue: Id 값의 생성 방법을 정의한다. GenerationType.AUTO는 자동생성을 의미한다.
  1. Repository 정의

  • CrudRepository: Repository를 확장한 interface로 기본적인 CRUD를 정의해 놓은 인터페이스.

여기까지 만들어 놓았으면 이제 자바 코딩은 일단락. 이제 스프링 설정을 보자.

  • entityManagerFactory: 자세히 몰라서 생략
  • p:packagesToScan: @Entity가 붙은 객체를 스캔하고 엔티티로 사용하도록 등록
  • p:jpaVendorAdapter: JPA에서 사용하는 orm 엔진(여기서는 hibernate를 이용)
  • p:jpaPropertyMap: hibernate가 구동하는데 필요한 정보 제공
  • jpa:repositories: Repository 인터페이스를 확장한 인터페이스를 찾아 컨벤션 규칙, 선언된 쿼리 기반으로 Data-Access 객체를 생성
  • jdbc:embedded-database: 스프링에서 사용자 편의를 위해 제공해주는 메모리 DB 설정 태그. 저리 하면 메모리 DB를 타겟으로하는 DataSource가 등록 마지막으로 의존 라이브러리 추가 (pom)

hibernate가 아니라 EclipseLink로 대체도 가능하다. 이제 끝! 이제 테스트로 돌리든, 웹앱으로 돌리든 시도하면 끗! 다음 포스트에 좀 더 설명을 해야지.

JibxMarshallerTests causes build failure on Windows

스프링이 gradle로 빌드 툴을 변경했다는 소식을 듣고 소스를 내려받고 빌드를 시도했다. 몇가지 테스트에서 에러가 났다.

1. 윈도우 7의 경우 콘솔창을 열 경우에 관리자 권한으로 실행해라. 그렇지 않으면 권한 문제로 테스트에 실패한다.

2.  JibxMarshallerTests 실패할 경우 ${basedir}/src/test/resources/org/springframework/oxm/jibx/binding.xml 파일을 열고 <binding> 엘리먼트에 name 속성을 추가해주면 된다.

<binding name=”binding”>

참고

 

spring-test-mvc 이용시 NoSuchMethodException 해결 방안

스프링 mvc 를 쉽게 테스트 가능하게 해주는 spring-test-mvc 라는 프로젝트가 나왔다. 이것을 이용하면 보다 상세하게 테스트를 할 수 있다.

예로 @RequestMapping 이라든지, 결과 페이지의 contents의 특정 값 또는 헤더 정보까지 확인이 가능하다.

spring-test-mvc의 자세한 설명은 생략한다.

여튼 이 프로젝트를 이용해서 테스트를 하려 하는데, 자꾸 다음 에러가 발생했다.

[java]java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V[/java]

이것이 뭔 소리지? 한 2시간 정도 고생하고, 스터디 멤버들에게 문의해서 원인을 찾을 수 있었다.

spring-test-mvc에서 내부적으로 사용하는 hamcrest(1.2.1)의 org.hamcrest.Matcher 클래스를 이용하는데, junit.jar 안에 org.hamcrest.Matcher와 이름이 동일해서 발생

내 추측으로는 패키지명 + 클래스명이 동일한 경우는 먼저 로딩된 파일이 유효한거 같다. 이 부분은 더 찾아보고 다시 포스트…

여튼, 이 문제는 간단히 해결이 가능하다.

maven을 사용하기 때문에 pom 파일에서 junit 보다 sprint-test-mvc 를 선언해주면 해결된다.

아 정말 별거 아냐~~~ 이걸로 몇시간을 날린거야… 여튼 하나 배웠군

cafe24에서 /wp없이 도메인에 워드프레스 연결하기

cafe24에서는 여러가지 프로그램을 자동 설치해주는 기능이 있다.

자동 설치된 결과물을 보면 다음과 같이 설치가 된다.

웹서비스의 루트가 /www 라면 /www/wp에 워드프레스가 설치된다.
(참고사항, 최근 워드프레스는 설치 디렉토리가 wp가 아닌 wordpress라면 /www/wordpress 로 변경해서 처리해줘야 한다.)

인터넷을 좀 아는 사람이라면 쉽게 알 수 있을 건데… 자동 설치 후 워드프레스 접속을 하려면 USER_DOMAIN + /wp/ 를 주소창에 입력을 해야 접속이 가능하다.

물론 이렇게 사용해도 큰 문제 없지만, 내 눈에는 참 거슬린다. (이전에는 루트를 내가 설정할 수 있는 환경이어서 wp가 없었고, 그 때 발행한 글의 링크 정보를 유지하려면 맞춰야 할 필요가 있었다.)

처음에는 무대뽀로 했다. 해당 서버에 접속해서 /www/wp 를 /www 에다가 강제로 다 옮겼다. 그랬더니 되긴 된다. 근데, 이건 나중에 부작용이 있을 수도 있고 추천하지 않는다.

워드프레스는 유저층이 굉장히 두터운 서비스인데, 이러한 기능이 없을리 없다. 역시나 워드프레스에서 쉽게 설정이 가능하다. 근데, 이러한 정보가 생각보다 적어서 고생하는 사람이 많을 것 같다. 부디 그런 사람들이 이 글을 보고 시간을 절약했으면 한다.

우선 워드프레스에서 설명하는 방법을 읽어보자. 이 글에 다 나와있다. 그럼 이 글대로 하는 방법에 대해서 설명하겠다.

1. 워드프레스 -> 관리자 -> 설정 -> 일반 – 아래 그림에 표시된 것처럼 2가지 정보를 수정하면 된다.

WordPress Address (URL): 설치된 곳을 가리키는 URL 정보를 입력. (예, 위에 언급한 내용처럼 /www 가 루트이고 이 하위에 /wp에 워드프레스가 설치되어 있다면 http://USER_DOMAIN + /wp/) Site Address (URL): 실제 주소창에 입력해서 넣길 원하는 정보를 입력한다.

도메인 뒤에 아무것도 붙지 않길 바란다면 아래 그림처럼 http://USER_DOMAIN으로 하면 된다.

2. index.php 복사 및 수정 – 워드프레스가 설치된 디렉토리를 보면 index.php라는 것이 있다. 이 파일을 복사(mv 아님)해서  웹 서비스 루트 디렉토리에 위치시킨다. 그리고 해당 파일을 열어 다음을 수정한다.

require(‘./wp-blog-header.php’);   ->  require(‘./wp/wp-blog-header.php’); 수정 내용중에 /wp 는 워드프레스가 설치된 디렉토리다. 자 이제 보기 싫은 /wp 나 설치 디렉토리 명이 URL에서 사라졌다. 즐거운 맘으로 블로깅질을 하자!  

윈도우 모바일 폰

2012년 1월, KT에서 윈도우 모바일 폰이 나왔다.
내가 담당 개발하고 있는 서비스는 아이폰, 안드로이드 계열 폰에 서비스를 제공하고, 다른 폰은 매몰차게 문전박대(PC 사이트로 이동) 하고 있었다.
KT의 자회사인 우리 회사는 KT폰에 서비스를 제공하라는 명을 하달받고 그 즉시 제공을 검토했다.

처음엔 유저 에이전트 기반의 필터링에서 제공하도록 필터링 항목에서 제외시켰다.
(Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; NOKIA; Lumia 710; Vodafone)

그리고 제공하는 서비스가 이상 없이 렌더링 되는걸 확인하고 안도하는 순간, 치명적인 문제를 찾아냈다.

1. 플리킹 기능 X.
– 애플웹킷 기반의 transition3d 를 이용해서 플리킹기능을 구현했는데, 윈도우 모바일 폰에서는 동작하지 않았다. 타 포털도 제공하지 않는걸로 봐서는 쉽게 제공이 어려울 것으로 보인다.
2. 터치이벤트 기반의 기능
– 무시무시한 내용. 터치 이벤트가 발생하지 않는다. 터치 이벤트 기반으로 기능 개발해둔 것은 다 무용지물이 됐다. ㅠ.ㅠ
3. 관련 정보 부재
– 너무너무(x100) 정보가 없다. 아직 레퍼런스나 그런게 많이 부족해 보인다.

일단은 소극적인 대응으로 렌더링을 제공하고, 가능한 범위 내에서 위에 언급된 문제를 줄여 나갈 예정이다.

XStream을 이용한 언마샬링 Java 모델 생성 방법

내가 일하는 곳에서는 데이터를 제공해주는 시스템이 노후화되어 있는 부분이 많다. 그래서 간단한 데이터도 대부분 XML형태로 되어 있고 well-formed 가 아닌 곳이 많다.

난 XStremam을 XML을 통해 받는 데이터를 Java 오브젝트로 변환하는데 사용한다. 이유는 생산성이 빠르기 때문이다. 요새는 Jaxb 2.x 에서도 annotation 기반으로 설정이 가능해서 유사하다고는 하는데, 난 아직 XStream만 써봤다. 이제 간단히 사용하는 방법이다.

Java 오브젝트로 변환할 XML 데이터
[xml]<user>
<name>Seosh</name>
<age>31</age>
<phone>012-3456-7890</phone>
<phone>987-654-3210</phone>
<career>
<company>Google</company>
<company>Facebook</company>
<company>White House</company>
</career>
</user>[/xml]

이제 이 정보를 담을 수 있는 Java 오브젝트를 만들겠다.

1. 루트 엘리먼트()에 대응되는 정보를 기술한다.
– @XStreamAlias를 이용. 클래스 이름과 상관없이 @XStreamAlias에 명시한 값과 루트 엘리먼트의 이름이 일치하면 된다.

2. name, age에 대응되는 정보를 기술한다. name, age는 하위에 자식 엘리먼트가 없기 때문에 객체의 멤버 변수로 지정하면 바로 정보를 매핑할 수 있다. 여기에는 2가지 방법이 있다. (String이 아닌 int나 다른 값을 명시하면 데이터를 Casting 해서 넣어준다. 변경 불가능한 값일 경우는 익셉션 발생하니 주의해야 한다.)
2.1. 멤버 변수 이름을 엘리먼트 이름과 일치해서 만든다.
2.2. 멤버 변수 이름과 엘리먼트 이름이 다를 경우에는 @XStreamAlias를 이용 @XStreamAlias(“엘리먼트 이름”) 이라 사용하면 된다.

3. phone 엘리먼트의 값을 받아오기. 제일 싫어하는 타입이다. 반복되는 엘리먼트인데 이를 하나의 집합으로 묶어주는 것이 없다. well-formed가 아닌 경우.
– 2처럼 단순히 Phone라는 멤버 변수로 받으려 하면 duplicateException이 발생한다. 이 말은 중복되는 엘리먼트니 하나의 멤버 변수에 대입이 불가능하다는 것이다. 이럴 때 사용하는 것이 @XStreamImplicit(itemFieldName = “phone”) 이다. 다음과 같이 사용하면 된다. itemFieldName에는 엘리먼트 이름을 대입한다.
[java]
@XStreamImplicit(itemFieldName = "phone")
List<String> phoneList;
[/java]

4. career 엘리먼트의 값 받아오기. XML을 보면 career는 자식 엘리먼트를 가지고 있다. 자식 엘리먼트가 있는 엘리먼트는 그냥은 정보를 받아올 수 없고 클래스로 정의해줘야 한다. 클래스를 다른 파일로 생성하게 되면 복잡한 XML은 파일의 갯수가 너무 많아지기 떄문에 스테틱 이너 클래스로 만드는 걸 추천한다.
– career 자식 레벨로 들어가게되면 아까 3번에서 한 것과 마찬가지로 company 엘리먼트가 중복된다. 이 부분은 위 3에서 언급했던 방식으로 코딩해주면 된다.
[java]
Career career;
static class Career {
@XStreamImplicit(itemFieldName = "company")
List<String> companyList;
}
[/java]

아래는 코딩된 모든 모델 파일의 정보.
[java]
@XStreamAlias("user")
public class User {
String name;
String age;
@XStreamImplicit(itemFieldName = "phone")
List<String> phoneList;

Career career;

static class Career {
@XStreamImplicit(itemFieldName = "company")
List<String> companyList;
}
}
[/java]

문자열을 거꾸로(reverse) 뽑아내기

문자열 조작에 대한 여러가지 요구사항이 있습니다. 그리고 이를 풀어내는 다양한 솔루션이 존재하구요.

이 범주에 속하는 것중에 하나가 문자열을 거꾸로 변환하는 질문인데요. 이 작업을 수행하기 위해서는 문자열을 배열로 변경하고 그 인덱스의 끝을 구해서 처음까지 리턴하는 방식으로 처리할 수 있습니다.

이 글에서는 이 방법이 아니라 JDK에서 제공하는 방법을 이용하는 솔루션을 소개합니다.

간단합니다. StringBuilder의 reverse()를 이용합니다.

[java]<span class="Apple-style-span" style="font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px; white-space: pre;">StringBuilder builder = new StringBuilder(str);</span>
<pre>return builder.reverse().toString();[/java]
위와 같이 하면 생성자의 파라미터로 넘겨진 str 문자열을 거꾸로 얻어오게 됩니다. 스레드 세이프하게 해야한다면 StringBuffer를 이용하시면 됩니다.
참 쉽죠?

Velocity 에서 for loop 사용하기

Java 에서 반복작업은 for loop을 사용해서 많이 했다.

Java 5 전에는 다음과 같이 for (int i=0; i++; i<n) {} 이런 식으로 사용했었다.

그리고 java 5에는 다음과 같은 문법이 추가되면서 좀 더 편해졌다.

for (String str : String[] strArray) {}

위 문법의 장점은 인덱스 없이 주어진 일련의 데이터에 대해서 동일한 작업을 할 수 있다는 것이다.

각설하고 여기서 말하고 싶은 것은 velocity 에서는 전자에서 말한 형태의 for 문은 존재하지 않는다. 다 후자와 유사한 구조로 되어 있는데, 때론 전자의 문법이 필요한 경우가 있다.

이럴 경우 range operand를 사용해서 해결할 수 있다. 사용 방법은 다음과 같다.

[java]#foreach($i in [1..$!{data.attend}])
<li>바보바보</li>
#end[/java]
형식은 #foreach($i in [m…n]) … #end 다
m과 n은 정수면서 크기가 다르거나 같은 정수이다. 뜻은 m 부터 n까지 일련의 작업을 진행해라이다.
만약 m>n 일 경우에는 1씩 감소하면서 반복하고, m<n일 경우는 1씩 증가하면서 반복한다.