'ITWeb/개발일반'에 해당되는 글 489건

  1. 2012.03.14 [egovframework] 개발자 개발환경 설치가이드.
  2. 2012.03.14 전자정부표준프레임워크 - 다운로드.
  3. 2012.03.13 Spring framework 프로젝트 UTF-8 설정.
  4. 2012.03.13 4. Spring MVC 에 Hibernate 적용해 보기.
  5. 2012.03.12 Spring Security <password-encoder>
  6. 2012.03.12 Spring Security InMemory + JDBCImpl 설정 방법.
  7. 2012.03.12 Spring Security JDBC 연동을 위한 Schema
  8. 2012.03.12 Spring Security login/logout 관련 글.
  9. 2012.03.12 3. Spring MVC 에 Spring Security 적용해 보기 1
  10. 2012.03.12 java interface 왜 사용하나요?

[egovframework] 개발자 개발환경 설치가이드.

ITWeb/개발일반 2012. 3. 14. 11:35
[참고문서]


일단 사용 소감은 그냥 샘플 템플릿을 이용해서 프로젝트 샘플이 후다닥 생성되고 바로 실행 결과를 볼수 있어서 쉽게 제공해주는 것 같습니다.

[수정한내용]

- DB 접속 정보 수정
- 제공되는 Schema DDL 을 이용해서 Table 생성
- 참고문서에 나와 있는 것처럼 mysql 을 사용하기 때문에 관련 정보 수정 


실행 하면 바로 화면 뜹니다.. ㅎㅎ



※ 그 이외는 그냥 spring mvc 기반으로 개발 하시면 될 것 같구요. 이제 전자정부의 콤포넌트를 사용해봐야겠내요..  
:

전자정부표준프레임워크 - 다운로드.

ITWeb/개발일반 2012. 3. 14. 09:56
전자정부표준프레임워크 다운로드 받는데 회원 가입을 시키다뉘...
뭐.. 암튼..
그래서 다운로드를 받았습니다.


용량이 커서 요기에는 못올리겠내요.. ^^;
정부 사업을 할라면.. 이런것도 알아 놔야 하겠죠!! 
:

Spring framework 프로젝트 UTF-8 설정.

ITWeb/개발일반 2012. 3. 13. 18:38
mybatis 랑 hibernate 로 작업 하면서.. insert 하다.. 한글이 다 깨져서 DB 에 들어가길래.. 설정 점검 차원에서 article 공유 합니다.

[참고사이트]

[기본]

- eclipse 설정
- web.xml
- servlet-context.xml
- DB 설정
- WAS 나 Web Server 설정 



[참고사이트 글 스크랩]

1. Tomcat 및 서블릿 컨테이너 encoding 설정


$CATALINA_HOME/conf/server.xml (이클립스 IDE를 사용한다면 Servers폴더 밑에 server.xml)을 수정한다.

Connector에 URIEncoding="UTF-8" 을 추가한다.



2. web.xml에 encoding filter를 만든다.




filter-class : org.springframework.web.filter.CharacacterEncodingFilter


3. ViewResolver의 property 속성중에 contentType을 UTF-8로 추가한다.


4. JSP페이지의 contentType을 UTF-8로 설정한다. 그리고 HTML의 content을 UTF-8로 설정한다.


 

 

 

==========================================================================================================================
==========================================================================================================================

 

 

개발을 하다보면 캐릭터셋문제로 골머리를 앓는 경우가 많이 있다.  필자는 그래서 신규로 프로젝트를 시작할 경우 아예 모든 환경을 UTF-8로 셋팅해 버린다. 물론 UTF-8까지 필요가 있을까 하는 생각을 할 수도 있지만 나중에 다국어환경을 고려해도 아예 UTF-8로 시작하는게 나쁜 방법은 아닌것 같다.
다음은 UTF-8로 개발환경을 셋팅하는 방법이다.

1. 이클립스의 Content type을 UTF-8로 설정
   - 이렇게 설정하면 해당하는 type을 파일로 저장할 때 UTF-8형식으로 저장하게 된다.



2. Workspace의 Text file encoding을 UTF-8로 선택
   - 이렇게 설정해야 svn의 소스비교시 캐릭터셋이 깨지는 문제를 막을 수 있다.



3. 데이터베이스 인스턴스를 UTF-8형태로 생성
  - Mysql같은 경우 아래와 같은 방식으로 생성하면 된다.

CREATE DATABASE database_utf8 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;


4. web.xml에 아래와 같이 인코딩 필터 셋팅
  - 서블릿과 통신할 때 데이터를 주고 받는 것에 대한 인코딩을 설정하는 부분이다.

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>
        org.springframework.web.filter.CharacterEncodingFilter
    </filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  </filter> 
  <filter-mapping>
      <filter-name>encodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping> 
</filter>


5. 톰캣설치디렉토리의 /conf/server.xml 에 URIEncoding 항목 추가
  - WAS와 get방식으로 데이터를 주고받는 것에 대한 인코딩 설정부분이다.

<Connector port="8080" URIEncoding="UTF-8" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" />


6. JSP파일의 최상단에 UTF-8 directive 추가
  - JSP컴파일시 사용할 인코딩을 설정하는 부분이다.

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>


7. JSP파일의 meta태그에 UTF-8추가
  - 브라우저에서 인식할 캐릭터셋을 설정하는 부분이다.

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>


8. 메이븐을 쓴다면 다음과 같이 plugin에 UTF-8 설정을 추가해 준다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.5</source>
        <target>1.5</target>
        <encoding>UTF-8</encoding>
    </configuration>
</plugin>
<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <configuration>
                <charset>UTF-8</charset>
                <docencoding>UTF-8</docencoding>
                <encoding>UTF-8</encoding>
                <quiet>true</quiet>
        </configuration>
</plugin>     
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-pmd-plugin</artifactId>
  <version>2.4</version>
  <configuration>
    <sourceEncoding>utf-8</sourceEncoding>
    <targetJdk>1.5</targetJdk>
    <minimumTokens>10</minimumTokens>
  </configuration>
</plugin>

 
:

4. Spring MVC 에 Hibernate 적용해 보기.

ITWeb/개발일반 2012. 3. 13. 16:18
※ 아래 코드에 delete 시 동작 안하는 문제가 있어서 코드 수정했습니다.
 

[contact.xml]

<id name="id" type="long"> // int 에서 long 으로 변경

 
[Contact*.java]

private Long id; // 역시 int 에서 Long 으로 변경 당연히 setter/getter/param 다 변경


[ContactDAOImpl.java]

// ...delete(contact); 가 정상적으로 동작하지 않아 아래 처럼 변경.
this.sessionFactory.getCurrentSession().createQuery("delete Contact where id=:id").setLong("id", id).executeUpdate();


[수정된 Project]
 proto.orm.zip





Spring MVC 에 다른 사이트의 HIbernate 예제 코드로 작성하였습니다.
- 해당 예제 코드를 그대로 사용할경우 안되는 부분이 있어서 직접 수정하여 반영합니다.

[Hibernate 예제코드]


[소스코드]

- Eclipse import 하시면 프로젝트 확인 가능 합니다.


※ import/export 방법은 아래 글 참고하세요.
http://jjeong.tistory.com/564 


1. Spring MVC 구성해 보기.
2. Spring MVC 에 MyBatis 적용해 보기.
3. Spring MVC 에 Spring Security 적용해 보기. 
4. Spring MVC 에 Hibernate 적용해 보기. 
5. 2+3번 적용해 보기.
6. 3+4번 적용해 보기. 


- 소스코드를 첨부 하였으므로 요약 정보만 기술 합니다.

[Table 생성]

Database : Mysql.protodb

CREATE TABLE CONTACTS

(

    id              INT PRIMARY KEY AUTO_INCREMENT,

    firstname    VARCHAR(30),

    lastname    VARCHAR(30),

    telephone   VARCHAR(15),

    email         VARCHAR(30),

    created     TIMESTAMP DEFAULT NOW()

); 



[pom.xml]

        <!-- spring framework jdbc 설정 -->

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-jdbc</artifactId>

            <version>${org.springframework-version}</version>

        </dependency>


        <!-- spring framework transaction 설정 -->

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-tx</artifactId>

            <version>${org.springframework-version}</version>

        </dependency>


        <!-- spring framework orm 설정 -->

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-orm</artifactId>

            <version>${org.springframework-version}</version>

        </dependency>


        <!-- hibernate dependency 설정 start -->

        <dependency>

            <groupId>org.hibernate</groupId>

            <artifactId>hibernate-core</artifactId>

            <version>3.6.9.Final</version>

        </dependency>


        <dependency>

            <groupId>dom4j</groupId>

            <artifactId>dom4j</artifactId>

            <version>1.6.1</version>

        </dependency>

        <dependency>

            <groupId>commons-collections</groupId>

            <artifactId>commons-collections</artifactId>

            <version>3.2.1</version>

        </dependency>


        <dependency>

            <groupId>org.javassist</groupId>

            <artifactId>javassist</artifactId>

            <version>3.15.0-GA</version>

        </dependency>


        <dependency>

            <groupId>cglib</groupId>

            <artifactId>cglib</artifactId>

            <version>2.2.2</version>

        </dependency>


        <dependency>

            <groupId>org.apache.commons</groupId>

            <artifactId>commons-lang3</artifactId>

            <version>3.1</version>

        </dependency>

        <!-- hibernate dependency 설정 end -->


        <!-- mysql -->

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.18</version>

        </dependency>


        <!-- apache commons dbcp -->

        <dependency>

            <groupId>commons-dbcp</groupId>

            <artifactId>commons-dbcp</artifactId>

            <version>1.2.2</version>

        </dependency>

- dependency 관련 파일들은 이제 어느 정도 설정 하는데 무리가 없으시죠. 자세한 내용은 아래 링크에


[web.xml]

 <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>

            /WEB-INF/spring/root-context.xml

            classpath:context/**/applicationContext*.xml

        </param-value>

    </context-param>
.... 중략

    <filter>

   <filter-name>openSessionInViewFilter</filter-name>

   <filter-class>

       org.springframework.orm.hibernate3.support.OpenSessionInViewFilter

   </filter-class>

    </filter>


    <filter-mapping>

        <filter-name>openSessionInViewFilter</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping> 



[applicationContext-hibernate.xml]

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"

    xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:util="http://www.springframework.org/schema/util"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd

        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd

        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">


    <bean id="messageSource"

        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

        <property name="basename" value="classpath:messages/message" />

        <property name="defaultEncoding" value="UTF-8" />

    </bean>


    <bean id="localeChangeInterceptor"

        class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">

        <property name="paramName" value="lang" />

    </bean>


    <bean id="localeResolver"

        class="org.springframework.web.servlet.i18n.CookieLocaleResolver">

        <property name="defaultLocale" value="ko" />

    </bean>


    <bean id="handlerMapping"

        class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">

        <property name="interceptors">

            <ref bean="localeChangeInterceptor" />

        </property>

    </bean>


    <bean id="propertyConfigurer"

        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"

        p:location="classpath:configuration/hibernate/config.properties" />


    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"

    destroy-method="close">

    <property name="driverClassName" value="${jdbc.driverClassName}" />

    <property name="url" value="${jdbc.url}" />

    <property name="username" value="${jdbc.username}" />

    <property name="password" value="${jdbc.password}" />

    </bean>


    <bean id="sessionFactory"

        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <property name="dataSource" ref="dataSource" />

        <property name="hibernateProperties">

            <props>

                <prop key="hibernate.dialect">${jdbc.dialect}</prop>

                <prop key="hibernate.show_sql">true</prop>

            </props>

        </property>

        <property name="mappingResources">

            <list>

                <value>/sql/hibernate/mapper/contact.xml</value>

            </list>

        </property>

    </bean>


    <bean id="transactionManager"

        class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory" />

    </bean>


    <tx:annotation-driven transaction-manager="transactionManager" />


    <bean id="contactDAO" class="proto.orm.board.contact.dao.ContactDAOImpl">

        <property name="sessionFactory" ref="sessionFactory" />

    </bean>

</beans>

- 간혹 아래와 같은 오류가 날때가 있습니다.

org.hibernate.HibernateException: No Session found for current thread

-> 이건 sessionFactory 설정을 잘못해서 나옵니다.

-> sessionFactory 를 중복 선언 하거나 (include 도니 파일 잘 찾아 보세요.)

-> web.xml 에 openSessionInViewFilter 설정이 빠져 있는지 확인 합니다.



Unable to instantiate default tuplizer 

-> 이건 domain 객체에서 setter/getter 설정 오류 이거나 mapping 오류일 경우 발생 합니다.

-> domain 객체를 한번 검토해 보시고 hibernate mapping xml 에 선언한 내용과 일치 하는지도 확인해 보세요.



org.hibernate.hql.ast.QuerySyntaxException: xxxx is not mapped [FROM xxxx]  

-> 이건  createQuery("FROM 도메인객체명") 으로 표기를 해야 하는데 간혹 Table 이른을 넣다 보면 오류가 납니다.

-> 본 예제에서는 Contact Object 이므로 createQuery("FROM Contact") 이라고 써줘야 합니다. 



[config.properties]

jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.dialect=org.hibernate.dialect.MySQLDialect

jdbc.url=jdbc:mysql://localhost:3306/protodb?autoReconnect=true

jdbc.username=root

jdbc.password=1234



[contact.xml]

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="proto.orm.board.contact.domain.Contact" table="contacts">

        <id name="id" type="int">

            <column name="id" />

            <generator class="identity" />

        </id>

        <property name="firstname" type="string">

            <column name="firstname" length="30" not-null="false" />

        </property>

        <property name="lastname" type="string">

            <column name="lastname" length="30" not-null="false" />

        </property>

        <property name="telephone" type="string">

            <column name="telephone" length="15" not-null="false" />

        </property>

        <property name="email" type="string">

            <column name="email" length="30" not-null="false" />

        </property>

        <property name="created" type="timestamp">

            <column name="created" not-null="false" />

        </property>

    </class>

</hibernate-mapping>

- 이 파일은 hibernate ref. 중 mapping 부분 참고하세요.



[ContactController.java]


[ContactServiceImpl.java] 


[ContactDAOImpl.java] 


[Contact.java]


[contact.jsp]


[참고사이트]

:

Spring Security <password-encoder>

ITWeb/개발일반 2012. 3. 12. 18:05
코드는 아래 처럼 사용하면 됩니다.

        <authentication-provider user-service-ref="userDetailsService">

            <password-encoder hash="md5" base64="true"/>

        </authentication-provider>

- 의미는 md5 hash 알고리즘을 사용하고,
- base64 인코딩을 해서 사용한다는 의미 입니다.
- 결국 client 에서 전달 되는 값들을 md5 로 암호화 하고 base64로 인코딩 후 password 비교를 하게 됩니다.
- DB 에 password 저장 시 당연히 md5 암호화를 하고 base64 encoding 후 저장을 해야 정상적으로 비교가 되겠죠..

[참고내용]
- URL :  http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#nsa-password-encoder 

B.2.4 <password-encoder>

Authentication providers can optionally be configured to use a password encoder as described in the namespace introduction. This will result in the bean being injected with the appropriate PasswordEncoder instance, potentially with an accompanying SaltSource bean to provide salt values for hashing.

Parent Elements of <password-encoder>

<password-encoder> Attributes

base64

Whether a string should be base64 encoded

hash

Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.

ref

Defines a reference to a Spring bean that implements PasswordEncoder .

Child Elements of <password-encoder>


:

Spring Security InMemory + JDBCImpl 설정 방법.

ITWeb/개발일반 2012. 3. 12. 17:22
가장 쉽게 접근할 수 있는 방법에 대해서 기술 합니다.
모든 내용의 기초는 아래 사이트에서 참고하였습니다.
- 없는건 검색했고요.. ㅎㅎ

 

[spring-security.xml InMemory] 

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

           http://www.springframework.org/schema/security

           http://www.springframework.org/schema/security/spring-security-3.1.xsd">


    <global-method-security secured-annotations="enabled" />


    <http auto-config='true'>

        <intercept-url pattern="/login.html*" access="IS_AUTHENTICATED_ANONYMOUSLY" />

        <intercept-url pattern="/welcome.html*" access="IS_AUTHENTICATED_ANONYMOUSLY" />

        <intercept-url pattern="/**" access="ROLE_ADMIN" />

        <form-login login-page='/login.html' default-target-url='/index.html' always-use-default-target='true' authentication-failure-url="/login.html" />

        <logout logout-success-url="/welcome.html" delete-cookies="JSESSIONID" />

        <remember-me key="daSDAsdaSDsa" />

    </http>


    <authentication-manager>

        <authentication-provider>

<!-- in memory case user-service start -->

            <user-service>

                <user name="jimi" password="1234" authorities="ROLE_USER, ROLE_ADMIN" />

                <user name="bob" password="1234" authorities="ROLE_USER" />

            </user-service>

<!-- in memory case user-service end -->

        </authentication-provider>

    </authentication-manager> 



[spring-security.xml JDBCImpl Case 1]
- InMemory 코드에서 빨간 부분이 변경 됩니다.

<!-- jdbcImpl case 1 start -->

            <jdbc-user-service data-source-ref="dataSource"

                users-by-username-query="SELECT username, password, enabled FROM users WHERE username=?"

                authorities-by-username-query="SELECT u.username, au.authority FROM users u, authorities au WHERE u.username=? AND au.username=u.username"

            />

<!-- jdbcImpl case 1 end -->



[spring-security.xml JDBCImpl Case 2]

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

           http://www.springframework.org/schema/security

           http://www.springframework.org/schema/security/spring-security-3.1.xsd">


    <global-method-security secured-annotations="enabled" />


    <http auto-config='true'>

        <intercept-url pattern="/login.html*" access="IS_AUTHENTICATED_ANONYMOUSLY" />

        <intercept-url pattern="/welcome.html*" access="IS_AUTHENTICATED_ANONYMOUSLY" />

        <intercept-url pattern="/**" access="ROLE_ADMIN" />

        <form-login login-page='/login.html' default-target-url='/index.html' always-use-default-target='true' authentication-failure-url="/login.html" />

        <logout logout-success-url="/welcome.html" delete-cookies="JSESSIONID" />

        <remember-me key="daSDAsdaSDsa" />

    </http>


    <authentication-manager>

<!-- jdbcImpl case 2 start -->

        <authentication-provider user-service-ref="userDetailsService" />

<!-- jdbcImpl case 2 end -->

    </authentication-manager>


<!-- jdbcImpl case 2 start -->

    <beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

        <beans:property name="dataSource" ref="dataSource"/>

    </beans:bean>

<!-- jdbcImpl case 2 end -->

</beans:beans>


※ 이넘들이 사용하는 Table Schema 는 아래 글 참고 하세요.
http://jjeong.tistory.com/610  

그리고 참고하기 위해서 JdbcDaoImpl.java 코드 올려 봅니다. 
이 코드를 보시면 이해 하는데 도움이 될겁니다.

※ 코드를 보시니 이제 감이 오시나요???
그렇습니다.
JdbcImpl.java 와 같은 넘을 biz 요구사항에 맞춰서 작성을 하시면 customized authentication 과 authority 를 구현 하실 수 있습니다. ^^ 
그래도 잘 모르시겠다구요.
그럼 아래를 참고하세요.
이미 정리 된 문서가 있어서 link 겁니다.

[원본링크]

 
:

Spring Security JDBC 연동을 위한 Schema

ITWeb/개발일반 2012. 3. 12. 15:50

[Spring Security JDBC 연동을 위한 Schema]

 CREATE TABLE users(

      username varchar_ignorecase(50) NOT NULL PRIMARY KEY,

      PASSWORD varchar_ignorecase(50) NOT NULL,

      enabled BOOLEAN NOT NULL);


 CREATE TABLE authorities (

      username varchar_ignorecase(50) NOT NULL,

      authority varchar_ignorecase(50) NOT NULL,

      CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username));

      CREATE UNIQUE INDEX ix_auth_username ON authorities (username,authority);

      

CREATE TABLE groups (

  id BIGINT generated BY DEFAULT AS identity(START WITH 0) PRIMARY KEY,

  group_name varchar_ignorecase(50) NOT NULL);


CREATE TABLE group_authorities (

  group_id BIGINT NOT NULL,

  authority VARCHAR(50) NOT NULL,

  CONSTRAINT fk_group_authorities_group FOREIGN KEY(group_id) REFERENCES groups(id));


CREATE TABLE group_members (

  id BIGINT generated BY DEFAULT AS identity(START WITH 0) PRIMARY KEY,

  username VARCHAR(50) NOT NULL,

  group_id BIGINT NOT NULL,

  CONSTRAINT fk_group_members_group FOREIGN KEY(group_id) REFERENCES groups(id));      

  

CREATE TABLE persistent_logins (

  username VARCHAR(64) NOT NULL,

  series VARCHAR(64) PRIMARY KEY,

  token VARCHAR(64) NOT NULL,

  last_used TIMESTAMP NOT NULL);  

 
※ 참고링크


[Mysql 용으로 변형] 

 CREATE TABLE users(

      username VARCHAR(50) NOT NULL,

      PASSWORD VARCHAR(50) NOT NULL,

      enabled BOOLEAN NOT NULL,

      PRIMARY KEY (username));


 CREATE TABLE authorities (

      username VARCHAR(50) NOT NULL,

      authority VARCHAR(50) NOT NULL

 );


CREATE UNIQUE INDEX ix_auth_username ON authorities (username,authority);

INSERT INTO users(username, PASSWORD, enabled) VALUE('henry', MD5('1234'), TRUE);   

INSERT INTO users(username, PASSWORD, enabled) VALUE('guest', MD5('1234'), TRUE); 

      

CREATE TABLE groups (

  id BIGINT(11) NOT NULL AUTO_INCREMENT,

  group_name VARCHAR(50) NOT NULL,

  PRIMARY KEY (id));


CREATE TABLE group_authorities (

  group_id BIGINT NOT NULL,

  authority VARCHAR(50) NOT NULL

);


CREATE TABLE group_members (

  id BIGINT(11) NOT NULL AUTO_INCREMENT,

  username VARCHAR(50) NOT NULL,

  group_id BIGINT NOT NULL,

  PRIMARY KEY (id));

  

CREATE TABLE persistent_logins (

  username VARCHAR(64) NOT NULL,

  series VARCHAR(64) PRIMARY KEY,

  token VARCHAR(64) NOT NULL,

  last_used TIMESTAMP NOT NULL);

- 참고로 제가 설치해서 사용하고 있는 mysql server 는 5.5 입니다.
- 5.6 부터는 base64 관련 함수가 포함이 되어 있더군요.. 흠..  
:

Spring Security login/logout 관련 글.

ITWeb/개발일반 2012. 3. 12. 15:21
기본적으로 아래 페이지를 한번 정독 하시면 어떻게 하는지 아실 수 있습니다.

그럼에도 불구하고 샘플코드가 잘 정리된게 있으면 좋겠죠.
굳이 제가 정리 하지 않아도 잘 정리된게 있어서 첨부 합니다.

[원본글 링크]

[Login 예제]

Defined your custom login form in Spring XML file. See explanation below :

  1. login-page=”/login” – The login form will be “/login”
  2. default-target-url=”/welcome” – If authentication success, forward to “/welcome”
  3. authentication-failure-url=”/loginfailed” – If authentication failed, forward to “/loginfailed”
  4. logout-success-url=”/logout” – If logout , forward to “/logout”

File : spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
 
	<http auto-config="true">
		<intercept-url pattern="/welcome*" access="ROLE_USER" />
		<form-login login-page="/login" default-target-url="/welcome"
			authentication-failure-url="/loginfailed" />
		<logout logout-success-url="/logout" />
	</http>
 
	<authentication-manager>
	  <authentication-provider>
		<user-service>
			<user name="mkyong" password="123456" authorities="ROLE_USER" />
		</user-service>
	  </authentication-provider>
	</authentication-manager>
 
</beans:beans>

In custom login form, you have to follow Spring Security standard name :

  1. j_spring_security_check – Login service
  2. j_spring_security_logout – Logout service
  3. j_username – Username
  4. j_password – Password

To display authentication error messages, use this :

${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}

File : login.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Page</title>
<style>
.errorblock {
	color: #ff0000;
	background-color: #ffEEEE;
	border: 3px solid #ff0000;
	padding: 8px;
	margin: 16px;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
	<h3>Login with Username and Password (Custom Page)</h3>
 
	<c:if test="${not empty error}">
		<div class="errorblock">
			Your login attempt was not successful, try again.<br /> Caused :
			${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
		</div>
	</c:if>
 
	<form name='f' action="<c:url value='j_spring_security_check' />"
		method='POST'>
 
		<table>
			<tr>
				<td>User:</td>
				<td><input type='text' name='j_username' value=''>
				</td>
			</tr>
			<tr>
				<td>Password:</td>
				<td><input type='password' name='j_password' />
				</td>
			</tr>
			<tr>
				<td colspan='2'><input name="submit" type="submit"
					value="submit" />
				</td>
			</tr>
			<tr>
				<td colspan='2'><input name="reset" type="reset" />
				</td>
			</tr>
		</table>
 
	</form>
</body>
</html>

File : hello.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
	<h3>Message : ${message}</h3>	
	<h3>Username : ${username}</h3>	
 
	<a href="<c:url value="/j_spring_security_logout" />" > Logout</a>
 
</body>
</html>



[Logout 예제]

In Spring Security, to log out, just add a link to url “j_spring_security_logout“, for example :

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
	<h3>messages, whatever</h3>	
	<a href="<c:url value="j_spring_security_logout" />" > Logout</a>
</body>
</html>

In Spring security, declares “logout” tag, and configure the “logout-success-url” attribute :

<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
 
	<http auto-config="true">
		<intercept-url pattern="/welcome*" access="ROLE_USER" />
		<logout logout-success-url="/welcome" />
	</http>
 
	<authentication-manager>
	  <authentication-provider>
	    <user-service>
		<user name="mkyong" password="password" authorities="ROLE_USER" />
	    </user-service>
	  </authentication-provider>
	</authentication-manager>
 
</beans:beans>



[Database Login]

1. Database Tables

In database, you need to create two tables to store user details and user role details, one to many relationship, one user can contains many roles.

A simple and standard table design for user role relationship. And, you are allow to add extra columns for extra functionality. In additional, the table name and column name are not fixed, you can rename to whatever name.

P.S All scripts in MySQL.

CREATE TABLE `users` (
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `USERNAME` VARCHAR(45) NOT NULL,
  `PASSWORD` VARCHAR(45) NOT NULL,
  `ENABLED` tinyint(1) NOT NULL,
  PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_roles` (
  `USER_ROLE_ID` INT(10) UNSIGNED NOT NULL,
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `AUTHORITY` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`USER_ROLE_ID`),
  KEY `FK_user_roles` (`USER_ID`),
  CONSTRAINT `FK_user_roles` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Insert data for testing, now an user “mkyong” is created and contains role named “ROLE_USER“.

INSERT INTO mkyongdb.users (USER_ID, USERNAME,PASSWORD, ENABLED)
VALUES (100, 'mkyong', '123456', TRUE);
 
INSERT INTO mkyongdb.user_roles (USER_ROLE_ID, USER_ID,AUTHORITY)
VALUES (1, 100, 'ROLE_USER');

2. Spring JDBC

Create a data source bean, and connect to database via Spring JDBC.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
   <bean id="dataSource"
	class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 
	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
	<property name="url" value="jdbc:mysql://localhost:3306/mkyongdb" />
	<property name="username" value="root" />
	<property name="password" value="password" />
   </bean>
 
</beans>

3. Spring Security

In Spring security configuration file, use “jdbc-user-service” tag, and define your query to get the data from database.

<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
 
	<http auto-config="true">
		<intercept-url pattern="/welcome*" access="ROLE_USER" />
		<form-login login-page="/login" default-target-url="/welcome"
			authentication-failure-url="/loginfailed" />
		<logout logout-success-url="/logout" />
	</http>
 
	<authentication-manager>
	   <authentication-provider>
		<jdbc-user-service data-source-ref="dataSource"
 
		   users-by-username-query="
		      select username,password, enabled 
		      from users where username=?" 
 
		   authorities-by-username-query="
		      select u.username, ur.authority from users u, user_roles ur 
		      where u.user_id = ur.user_id and u.username =?  " 
 
		/>
	   </authentication-provider>
	</authentication-manager>
 
</beans:beans>




:

3. Spring MVC 에 Spring Security 적용해 보기

ITWeb/개발일반 2012. 3. 12. 12:40
Spring MVC 에 기본 Minimal http configuration 에대한 소스 코드만 삽입해 놓습니다.
확장 버전은 직접 해보시는면 좋겠죠.
- 다만, 진행 하면서 코드는 추가 하겠습니다.

[소스코드]

- Eclipse import 하시면 프로젝트 확인 가능 합니다.


※ import/export 방법은 아래 글 참고하세요.
http://jjeong.tistory.com/564 


1. Spring MVC 구성해 보기.
2. Spring MVC 에 MyBatis 적용해 보기.
3. Spring MVC 에 Spring Security 적용해 보기. 
4. Spring MVC 에 Hibernate 적용해 보기. 
5. 2+3번 적용해 보기.
6. 3+4번 적용해 보기. 


- 소스코드를 첨부 하였으므로 요약 정보만 기술 합니다.

[참고링크]


[pom.xml]
- spring security 관련 dependency 추가 합니다.

<!-- Spring Security 사용을 위한 dependency 등록 Start -->

        <dependency>

            <groupId>org.springframework.security</groupId>

            <artifactId>spring-security-core</artifactId>

            <version>3.1.0.RELEASE</version>

        </dependency>

        <dependency>

            <groupId>org.springframework.security</groupId>

            <artifactId>spring-security-web</artifactId>

            <version>3.1.0.RELEASE</version>

        </dependency>

        <dependency>

            <groupId>org.springframework.security</groupId>

            <artifactId>spring-security-config</artifactId>

            <version>3.1.0.RELEASE</version>

        </dependency>

<!-- Spring Security 사용을 위한 dependency 등록 End -->



[web.xml]
- security 관련 fitler 와 context 설정을 등록 합니다.

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>

        /WEB-INF/spring/root-context.xml

        classpath:context/**/applicationContext*.xml

        </param-value>

    </context-param>


<!-- Spring Security 사용을 위해 filter 추가 Start -->

    <filter>

        <filter-name>springSecurityFilterChain</filter-name>

        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

    </filter>


    <filter-mapping>

        <filter-name>springSecurityFilterChain</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

<!-- Spring Security 사용을 위해 filter 추가 End -->



[application-security.xml]
- http security 관련 설정을 합니다.
- 메모리 기반의 인증 처리 방식 입니다. (추후 업그레이드가 되어야 하는 부분)

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

           http://www.springframework.org/schema/security

           http://www.springframework.org/schema/security/spring-security-3.1.xsd">


    <http auto-config='true'>

        <intercept-url pattern="/**" access="ROLE_USER" />

    </http>


    <authentication-manager>

        <authentication-provider>

            <user-service>

                <user name="jimi" password="1234" authorities="ROLE_USER, ROLE_ADMIN" />

                <user name="bob" password="1234" authorities="ROLE_USER" />

            </user-service>

        </authentication-provider>

    </authentication-manager>

</beans:beans>



[실행하기]
- Spring MVC 예제에서 했던 것과 같습니다.
- 이전 글을 참고해 주세요.

- 아래는 실행 시킨 결과 화면 입니다.

[테스트URL]
http://localhost:8080/security/index.html 

[인증 전]

 
[인증 후] 

 


※ 보시는 것 처럼 spring security의 시작은 아주 쉽게 따라 할 수 있습니다. 문서를 잘 참고 하시면 됩니다.
:

java interface 왜 사용하나요?

ITWeb/개발일반 2012. 3. 12. 11:18
[interface 는 왜 사용하나요?]
- 요기에 토론이 있내요. : http://www.okjsp.pe.kr/seq/161248 


[개인적인 의견]

interface 를 사용하는 건 개인적으로
- 개발한 코드의 종속성을 줄이고 유지보수성을 높이기 위함이 아닐까 싶습니다.
- 뭐 설계를 잘해야 하는건 당연한 말이겠지만 가져다 사용하는 사람 입장에서 뒤에 뭐가 바뀌었다고 앞에 뭐를 고쳐야 한다고 하면.. 이게 삽질이 아니고 뭐겠습니까...

암튼 전 어려운말 별로 안좋아 해서.. 그냥 유지보수의 편의성이라고 우겨 봅니다. ㅎㅎ 



[Interface 의 다중 type 이해]

직접 인터페이스를 선언해서 사용해 보자.

public interface Book{

    private String author;

    private String title;

    public void publish(){

        //출판

    }

}


public class Cartoon implements Book{

    //author와 tittle에 접근 할 수 있음

    public void publish(){

        // 꼭 구현해야함

    }

}


public class Novel implements Book{

    //author와 tittle에 접근 할 수 있음


    public void publish(){


        // 꼭 구현해야함


    }

}


이제 부터 진짜 중요한 부분이다. 이 부분을 이해해야 객체 지향의 진정한 의미를 알게 되는 것이다.


Cartoon c = new Cartoon();

Novel n = new Novel();


보통 일반적으로 저렇게 많이 선언해서 사용한다.

하지만 이렇게 한 번 해보자.

Book c = new Cartoon();

c.publish();//만화책 출판


Book n = new Novel();

n.publish();//소설책 출판


위에 의미를 이해 하겠는가? 클래스 형식이 인터페이스 형식인것이다.

Type이 인터페이스가 될 수 있다. 이렇게 됐을 때 인터페이스의 메소드를 호출하게 되면 실제는 생성된 클래스의 구상 메소드가 호출된다.

인터페이스라는 것을 통해서 클래스의 기본 틀을 얻을수 있으며 구상 클래스들에 접근할수 있다는 것을 보았다.


이렇게 하면 좋은 점이 무엇이냐 하면 구상 클래스에 의존하지 않은 인터페이스에 의존하는 프로그램을 작성할 수 있다는 것이다. 인터페이스에 의존한다는 것은 쉽게 말해서 고정된 틀에 얽매이지 않아도 되는 것이다. 물론 인터페이스의 틀에는 얽매이겠지만 적어도 공통된 틀이기 때문에 어느 클래스에도 접근할 수 있다는 것이다.



[Polymorphism]

0. 개요

지금껏 객체지향의 꽃은 '다형성'이라는 말을 많이 들어봤을 것이다. Java 내에서도 이 다형성이라는 놈을 구현하기 위해 여러 가지 장치를 마련해놓았다. 사실 다형성이란 단어를 한 마디로 압축해서 정의하기는 어렵지만, 필자가 지금껏 Java를 공부하면서 느낀 '다형성'은 '융통성', '확장성'과 일맥상통한다고 말하고 싶다. 이번 강좌는 '융통성(확장성)'에 촛점을 두고 다형성에 대한 이야기를 풀어나가고자 한다.

1. 사전적 의미
다형성에 대해 백과사전에서 찾아보면 동질이상, 즉 화학적인 조성은 같은데 결정구조에 따라 형태가 달라지는 현상을 언급하고 있다. 이를테면 CaCo₃과 같이 화학식은 같은데, 입체적인 구조에 따라 방해석과 아라고나이트로 형태가 달라지는 식이다. 이러한 내용을 정확히 프로그래밍에 대입하기는 어렵겠지만, 프로그래밍에서 넓은 의미의 다형성이라고 한다면 '한가지 표현 방식으로 여러 가지 기능을 수행할 수 있는 것'이라고 표현할 수 있다.

2. Interface
넓은 의미에서의 인터페이스란 외부와의 의사 소통을 위한 매개체이다. 의사 소통을 위해서는 외부와의 정해진 약속이 있어야 할 것이다. 이를 강조하는 의미에서 예전 강좌에서 인터페이스를 '표준 규격'이라고 설명한 적이 있다.

인터페이스를 정의하고 활용하는 이유는 물론 인터페이스를 구현하는 부품들이 중구난방이 되지 않기 위한 최소한의 규약을 정하기 위해서이지만, 이를 약간 넓게 생각해본다면 확장의 여지가 있는 부분을 클래스로 제작해 닫아버리기보다는 인터페이스로 선언하여 최소한의 선을 정해놓고 융통성을 발휘하고자 하는 의도도 포함되어 있다.

(1) 표준 규격

간단한 예로 컴퓨터에 키보드나 마우스를 연결한다고 생각해보자. 우리는 단지 키보드와 컴퓨터를 연결하는 단자가 PS/2 형식인지, 혹은 USB 형식인지를 확인하면 된다. 여기서 말하는 PS/2나 USB 포트는 일종의 규격이며, 형식에 맞게 제작된 키보드나 마우스는 해당 단자에, 구멍에 맞게 끼우기만 하면 어느 회사에서 만들었든간에 아무런 문제 없이 사용이 가능하다. 예전에 beginning 강좌에서 들었던 220V 콘센트의 예도 마찬가지일 것이다. Java 프로그래밍에서의 인터페이스의 기본 개념도 이러한 예들과 흡사하다.

인터페이스의 기본적인 의미는 인터페이스를 구현한 클래스들을 그룹화하고, 해당 그룹의 필수 메서드들을 선언함으로써 그룹을 형성하는 클래스들에게 외부와의 대화 방침을 대내외적으로 알리고자 하는 것이다. 인터페이스는 개발자에게는 인터페이스의 내용을 충실히 구현할 의무를 부여하고, 사용자에게는 인터페이스의 내용만 알면 해당 그룹의 클래스를 세부 명세 없이도 인터페이스 명세에 따라 사용할 수 있다는 메리트를 제공한다. 더 깊이 생각해보면 인터페이스는 본질적으로 encapsulation과 이어져 있다. 즉, 외부와의 대화를 인터페이스를 통해서만 할 수 있도록 하는 것이다.

(2) 확장성

사실 인터페이스는 공동 작업을 위해 존재한다. 혼자서 프로그램의 모든 부분을 코딩하고 수정하는 수준의 작은 규모의 작업이라면 인터페이스는 그다지 필요가 없다. 그래서 표준 규격이라는 비유가 적절한 것인지도 모르겠다. 실제로 Java에서 제공하는 기본 API를 살펴보면 이들 사이 인터페이스의 체계적인 모습에 감탄하게 될 것이다. 그러나 인터페이스는 단순히 규격을 정하는 데에서 그 의미를 다하지 않는다. 잘 선언된 인터페이스는 항상 확장성을 염두에 두고 있다.

예를 들자면 Review 강좌의 두번째 글의 예와 같을 것이다. logging에 대한 출력을 콘솔, 파일, DB, Mail 등 여러 가지 방식으로 할 수 있는데, 여기에 신속한 장애 대처를 위해 ERROR 레벨 로그를 관리자에게 SMS로 보내는 방법을 추가하고자 하는 상황이 있을 수 있다. 이때 logging 출력 라이브러리들을 인터페이스로 그룹화해서, 이들이 공통으로 구현해야 하는 로그 찍기 메서드를 선언만 해두었다면, SMS 출력용 클래스를 logging 인터페이스에 맞게 구현만 하면 간단하게 확장이 될 것이다.

(3) 인터페이스와 형변환, 그리고 다형성

흔히 상속, 인터페이스와 관련해서 다형성을 설명할 때 '상위 클래스 타입의 객체 참조 변수에 하위 클래스 인스턴스를 연결할 수 있다'고 말한다. 이제 위에서 설명한 내용을 생각해보면서 이 문장을 구체적으로 살펴보도록 하자.

// 인터페이스 Eproduct : 가전제품을 총칭하는 인터페이스
interface Eproduct { void disp(); }

// 가전제품 인터페이스를 구현하는 클래스들
TV implements Eproduct {

   int channel = 11;
   public void disp() { ... 저장된 채널값을 바탕으로 해당 채널을 TV 화면에 보여주는 내용 ... }
   public int setChannel(int ch) { this.channel = ch; }
   ... 다른 구현부

}

CDplayer Implements Eproduct {

   int cdNumber = 1;
   public void disp() { ... 오디오 CD의 현재 곡 번호를 액정에 표시하는 내용 ... }
   public int setCdNumber(int cdNum) { this.cdNumber = cdNum; }
   ... 다른 구현부

}

TV와 CD플레이어 클래스는 모두 가전제품 인터페이스를 구현하고 있다. 이들 클래스에서 인스턴스를 뽑아내는 방법은 다음의 두가지이다.

(a) TV tv = new TV(); CDplayer cdp = new CDplayer();
(b) Eproduct tv = new TV(); Eporduct cdp = new CDPlayer();

disp() 메서드를 활용하는 리모콘을 구현하는 메서드를 다음과 같다고 하자.

   remocon(Eproduct thing) { thing.disp(7); }

이제 remocon() 메서드에 tv와 cdp가 매개변수로 들어가는 경우를 나누어서 생각해보자.

(a) 자손형 객체변수를 인터페이스형 파라미터 공간에 대입하게 되면, 임시 upcasting이 일어난다. 즉, 매개변수 thing은 Eproduct형이지만, 결론적으로는 자손형 인스턴스를 참조할 수 있다.
(b) (a)에서 임시로 형변환이 이루어지는 과정을 아예 인스턴스 생성 부분에서 표현하고 있다. 즉, Eproduct 타입인 tv 객체 참조 변수로 TV 타입 인스턴스를 참조하고 있는 것이다. 이 경우 tv 변수로 TV 인스턴스의 setter 메서드인 setChannel()에 접근할 수 없다. (a)와 같은 경우에도 remocon 메서드 안에서 thing 지역 변수는 절대로 setChannel()이나 setCdNumber()에 접근할 수 없다.

(c) 그렇다면 다음과 같은 예는 어떠한가?

Eproduct tv = new TV();
TV realTV = (TV)tv;
realTV.setChannel(9);

결론만 이야기하자면 realTV 변수는 tv가 가리키는 인스턴스의 모든 것을 다시금 접근할 수 있게 된다. tv는 단지 Eproduct형 객체 참조 변수이기 때문에 TV 인스턴스의 고유 메서드에 접근할 수 없을 뿐이지, 인스턴스 자체에 손실이 있는 것은 아니다. 따라서 TV형 참조 변수를 이 인스턴스에 연결하는 것도 합법이며, RealTV는 아무런 손실 없이 tv가 가리키고 있던 TV형 인스턴스의 고유 메서드를 모두 사용할 수 있게 된다. 위와 같은 예를 downcasting이라고 하는데, (b)와는 달리 강제로 형변환하지 않으면 대입 자체가 불가능해진다.

(d) 확장성과 연관지어 생각해보자면, 위의 리모콘 메서드는 가전제품 인터페이스를 구현하는 두 종류의 인스턴스를 매개변수로 받고 있다. 가전제품 인터페이스를 구현하는 다른 클래스, 이를테면 MicowaveOven이라던가 AirConditioner와 같은 새로운 클래스를 제작하게 되더라도 리모콘 메서드에 변화를 주지 않고 Eproduct형으로 인스턴스를 받을 수 있을 것이다. 가전제품 인터페이스를 활용하는 개발자 입장에서는 전자렌지나 에어컨이 구현하는 인터페이스의 내용 disp()의 세부 구현 사항을 알 필요가 없다. 그저 이들의 disp()를 호출했을 때 '액정이나 화면에 핵심 정보를 출력하는 기능'을 수행한다는 인터페이스 명세서를 가지고 있으면 된다.

3. 상속, 인터페이스에서의 유의점 몇 가지
복습하는 김에 몇 가지 정리해보고 가자.

(1) 상속 (extends)

□ 기능 확장, 코드 재사용의 개념이다. 필드와 메서드를 상속받는다. 생성자는 상속되지 않는다.
□ 하위 클래스의 인스턴스가 생성될 때 자동으로 상위 클래스의 default 생성자가 호출된다. (super();)
□ 상위 클래스에 default 생성자를 기술하지 않았다면 다른 매개변수를 갖는 상위 생성자를 반드시 호출해야 한다.
□ 하위 클래스의 생성자에서 상위 클래스의 인수를 갖는 생성자를 호출하기 위해서 super(arg0, ..., argn);를 기술한다.
□ 메모리에는 상위 클래스의 메서드와 하위 클래스의 메서드가 모두 저장되어 있다.
□ 메서드 오버라이딩 시에 하위 클래스의 메서드가 우선권을 가진다. 상위 클래스의 메서드에 접근할 때에는 super를 써야 한다.
□ C++에서는 메서드 오버라이딩 시 상위 함수에 virtual 키워드를 적용해야 Java와 같은 방식의 메서드 동적 호출이 가능해진다.
□ 오버라이딩 시 하위 클래스 메서드의 접근 지정자는 부모 클래스 메서드의 접근 지정자 범위보다 크거나 같아야 한다.
□ 위 범위 공식은 메서드가 throws로 예외를 던질 때 예외 값의 범위에도 그대로 적용된다.

(2) 추상클래스 (abstract)

□ 추상메서드를 포함한다. (구현x) => 인스턴스 생성 불가, 상속받은 클래스를 통해 인스턴스를 생성한다.
□ 상속된 하위 클래스의 인스턴스 생성 시에 다형성 구현이 가능하다. 추상클래스형 변수 = new 하위클래스형 인스턴스;
□ 상속된 하위 클래스에서 추상 메서드를 반드시 재정의한다.

(3) 인터페이스 (interface)

□ 상수 필드 + 추상 메서드의 구조이다. 상수는 public static final, 추상 메서드는 public이며 생략해도 자동으로 추가된다.
□ 인터페이스를 상속받는 인터페이스 작성 가능 -> extends를 사용한다.
□ 인스턴스 생성 불가. 구현(상속) 클래스를 통해 인스턴스를 생성한다. 추상클래스와 마찬가지로 다형성 구현이 가능하다.
□ 추상으로 선언되어 구현되지 않은 모든 메서드는 인터페이스 제작자의 의도인 셈이다. 모든 메서드를 반드시 재정의할 것.
□ 인터페이스 선언 시에 생성자를 명시하지 않고 구현 클래스 또한 기본 생성자가 없는 경우 default 생성자가 자동 생성된다.
□ 그러나 구현 클래스에서 매개변수가 있는 생성자를 만들고 default 생성자를 구현하지 않으면 상속과 마찬가지로 error 발생.

4. Generics
Generics에 관한 내용을 첨가할까 말까 하다가 간단히만 설명하고 넘어가기로 결정했다. 자세한 내용은 추후에 다시 다뤄볼 생각이다. Generics는 C++의 템플릿과 같은 개념이다. (C#에서는 아예 Template이 Generics로 바뀌었으니 일치하는 내용이라고 봐도 무관할 것이다) Generics를 설명할 수 있는 가장 쉬운 예로는 Collection 계열 클래스에서 흔히 사용되는 List와 같은 컨테이너 인터페이스가 있다.

List myIntList = new LinkedList();
myIntList.add(new Integer(0));
Integer x = (Integer).myIntList.iterator().next();

myIntList는 List 인터페이스 타입으로 LinkedList 인스턴스를 생성하고 있다. Integer(0)은 정수 0을 Integer 클래스로 객체화되어 감싸진다.(wrapping) myIntList에 이 인스턴스를 넣고, 다시 Iterator로 뽑아내고 있다. 이 과정에서, List 인터페이스의 add는 Object형 매개변수를 받고 있으므로 Integer(0)은 upcasting이 되어 myIntList라는 컨테이너에 들어가고, Iterator에 의해 나올 때에도 Object형으로 리턴된다. 3행에서 리턴된 값을 (Integer)로 형변환하는 것을 주목해보자. 이렇게 downcasting해주지 않으면 Integer x 에 대입할 수가 없다.

문제는 바로 여기서 발생한다. 3행에서의 강제 형변환은 프로그램을 난잡하게 할 뿐만 아니라, 런타임 에러의 가능성을 발생시킨다. 위의 코드조각에서는 (Integer) 강제 형변환으로 해결하고 있지만, 사실 프로그래머의 의도대로 돌아가기 위해서는 컨테이너 차원에서 "이 컨테이너는 Integer형 인스턴스만을 저장할 수 있다"라고 명시하는게 논리적일 것이다. 애초에 리스트에 특정 타입만 들어갈 수 있도록 강제하는 것, 이것이 Generics의 핵심 개념이다. Generics를 이용해서 위의 코드를 바꾼다면 아래와 같을 것이다.

List<Integer> myIntList = new LinkedList<Integer>();
myIntList.add(new Integer(0));
Integer x = myIntList.iterator().next();

이제 강제 형변환 없이 논리적으로 리스트를 규정하고 활용하는 코드 조각이 완성되었다. 이러한 경우 List는 Integer를 받는 generic 인 터페이스라고 한다. 이 글에서 Generics에 관해 간략히 언급하고 넘어가는 이유는, 인터페이스와 관련된 형변환이 주로 컨테이너 클래스를 사용할 때 빈번히 일어나기 때문이다. 다형성과 연관지어 이런 내용이 있다는 정도만 알아두고, 나중에 generic 인터페이스를 작성하는 방법에 대해 구체적으로 살펴보도록 하자.

5. 광의의 다형성
우리가 보통 Java의 다형성을 이야기할 때 위의 내용을 주로 언급하지만, 넓은 의미로 살펴보면 보다 많은 다형성의 예를 살펴볼 수 있다. 이를테면 메서드의 매개변수의 다양성을 보장해주는 Overloading 또한 넓은 의미의 다형성 범주에 포함될 것이다. 예전 beginning 강좌 초반에 다형성의 예로 오버로딩을 든 것은 보다 쉽게 다형성의 의미를 심어주기 위함이었지만, 사실 Java에서의 다형성의 핵심은 상속과 인터페이스에서의 메서드 오버라이딩이라는 것을 이제는 알 수 있을 것이다.

: