Spring 프레임워크 소개와 IoC 및 Spring IoC의 개념

Toggle Space Navigation Tree
Space Map

Spring 프레임워크 소개와 IoC 및 Spring IoC의 개념

0. 목차

0. 머리말

이 문서에서는 Lightweight Container의 선두주자라 할 수 있는 Spring Frmaework에 대한 간단한 소개와
Spring Framework의 기본적인 개념이라고 할 수 있는 IoC 그리고 Spring Container에서 사용되고 있는 IoC에 대하여 알아본다.
IoC의 개념은 OOP에서는 이미 고전적인 이론이지만 몇 년 사이에 DI 툴들의 확산과 여러 아티클 등에서의 언급으로 사람들의 많은 관심을 받게 되었다.
따라서 이론을 소개한 문서 및 예제들이 많이 있는 관계로 본 문서에서는 혼란스러운 부분을 없애고자
자바지기 박재성님의 책 Spring 프레임워크 워크북의 내용을 근간으로 작성되었음을 밝힌다.
문서를 보면서 '''! 이거 책 그대로 베꼈잖아.''' 오해를 하시는 분이 계실까봐 미리 언급을 하지만 베낀 것이 아니라
위 책을 기준으로 요약 정리를 한 것으로 생각하면 좋을 것이다.(해주세요..ㅡ.ㅡ+)
또 위키안에 잘 정리되어 있는 문서들이 많으므로 필요할 경우 불필요한 중복을 피하고자 링크로 대신하기로 하겠다.
(이 시점에서 짧은 식견과 글솜씨가 그다지 뛰어나지 않은 작성자의 아픔을 헤아려주는 센스가 필요하겠다..^^;..아!! 이 궁색한 변명..ㅡ.ㅡ)

1. Spring Framework 개요

1.1 아키텍쳐의 발달과정

1.1.1 Non EJB 아키텍쳐

  • 장점
    • 별도의 교육없이 초보 개발자들도 쉽게 개발하는 것이 가능.
    • 초기 개발 속도가 빠르므로 비교적 단시간에 가시적인 성과 가능.
    • Servlet Container 만으로 운영 가능.
    • Application Server, Servlet Engine에 대해 더 좋은 이식성.
    • 개발주기가 짧기 때문에 개발 생산성의 향상을 가져옴.
  • 단점
    • 초기 개발 속도가 빠르지만 추가 요구사항이나 변경사항에 대응속도가 느림.
    • 명확한 계층분리가 어렵고 중복코드가 많이 발생할 확률이 높다. 이로 인한 비용소요도 높다.
    • 표준화된 환경과 관리 부족. 유지 보수의 어려움.
    • 분산환경을 지원할 수 없다. 필요한 기능이 있을 때마다 구현 필요.
    • 비즈니스 POJO 객체의 생명주기를 프로그램으로 제어해야 한다.
    • EJB가 지원하는 선언적인 트랜잭션과 같은 기능을 사용할 수 없다.

1.1.2 EJB 아키텍쳐

  • 장점
    • 정형화된 Service Layer 제공. 논리, 물리적인 분리가 가능.
    • 선언적인 Transaction 관리와 같은 EJB 서비스 제공
    • Business Object를 여러 서버에 분산이 가능
    • 다양한 클라이언트에 대한 지원 가능.
  • 단점
    • 실행속도와 개발속도등 여러 부분에서 상당한 overhead가 발생함.
    • 개발 사이클이 복잡한 관계로 개발 생산성 저하.
    • 테스트하기 어려움. 항상 EJB Container에서만 테스트가 가능함.
    • EJB의 실행 속도 해결을 위한 변형된 패턴들이 나타나면서 객체지향적 개발에 제약사항이 됨.
    • 대형 벤더들의 고유기능으로 인하여 컨테이너 사이의 이식성이 떨어짐.

1.1.3 Lightweight Container 아키텍쳐

  • 장점
    • EJB보다 배우기 쉬우며, Configuration 또한 쉽다.
    • Application Server와 Servlet Engine의 이식성 높음.
    • Servlet Engine에서 실행이 가능하므로 이식성이 높음.
    • IOC(Inversion of control)을 통한 Business Object의 관리가 용이함.
    • AOP의 지원으로 인해 선언적인 Transaction 관리와 같이 EJB에서 지원하던 기능들의 지원이 가능함.
    • 특정 인터페이스에 종속되지 않은 POJO를 기반으로 하기 때문에 테스트가 용이함.
    • OOP 형태로 개발하는데 제한이 없음.
  • 단점
    • Remote 분산환경을 지원하지 않음. Web service를 통하여 해결 가능.
    • 현재 Lightwegiht Container에 대한 표준이 없음.
    • EJB 아키텍트와 개발자들에 친숙하지 않음.

1.2 Spring 프레임워크 개요

  • Spring 프레임워크 구성
    • Spring Core
      Spring 프레임워크의 근간이 되는 IoC(또는 DI) 기능을 지원하는 영역을 담당하고 있다.
      BeanFactory를 기반으로 Bean 클래스들을 제어할 수 있는 기능을 지원한다.
    • Spring Context
      Spring Core 바로 위에 있으면서 Spring Core에서 지원하는 기능외에 추가적인 기능들과 좀 더 쉬운 개발이 가능하도록 지원하고 있다.
      또한 JNDI, EJB등을 위한 Adaptor들을 포함하고 있다.
    • Spring DAO
      지금까지 우리들이 일반적으로 많이 사용해왔던 JDBC 기반하의 DAO개발을 좀 더 쉽고, 일관된 방법으로 개발하는 것이 가능하도록 지원하고 있다.
      Spring DAO를 이용할 경우 지금까지 개발하던 DAO보다 적은 코드와 쉬운 방법으로 DAO를 개발하는 것이 가능하다.
    • Spring ORM
      Object Relation Mapping 프레임워크인 Hibernate, IBatis, JDO와의 결합을 지원하기 위한 기능이다.
      Spring ORM을 이용할 경우 Hibernate, IBatis, JDO 프레임워크와 쉽게 통합하는 것이 가능하다.
    • Spring AOP
      Spring 프레임워크에 Aspect Oriented Programming을 지원하는 기능이다. 이 기능은 AOP Alliance 기반하에서 개발되었다.
    • Spring Web
      Web Application 개발에 필요한 Web Application Context와 Multipart Request등의 기능을 지원한다.
      또한 Struts, Webwork와 같은 프레임워크의 통합을 지원하는 부분을 담당한다.
    • Spring Web MVC
      Spring 프레임워크에서 독립적으로 Web UI Layer에 Model-View-Controller를 지원하기 위한 기능이다.
      지금까지 Struts, Webwork가 담당했던 기능들을 Spring Web MVC를 이용하여 대체하는 것이 가능하다. 또한 Velocity, Excel, PDF와 같은 다양한 UI 기술들을 사용하기 위한 API를 제공하고 있다.

2. IoC

2.1 IoC(Inversion of Control)이란 무엇인가?

자바가 등장하고 자바 기반으로 애플리케이션을 개발하기 시작하던 최초의 시기에는 자바 객체를 생성하고 객체간의 의존관계를
연결시키는 등의 제어권을 개발자가 직접 가지고 있었다. 그러나 서블릿, EJB가 등장하면서 개발자들의 독점적으로 가지고 있던
제어권이 서블릿과 EJB를 관리하는 컨테이너에게 넘어가 버렸다. 객체에 대한 제어권이 컨테이너에게 넘어가면서 객체의 생명주기를
관리하는 권한 또한 컨테이너들이 전담할 수 밖에 없게 되었다. 이처럼 객체의 생성에서부터 생명주기의 관리까지 모든 객체에 대한
제어권이 바뀐것을 의미하는 것이 제어권의 역전, 즉 Ioc라는 개념이다.

2.2 Martin Fowler의 Inversion of Control Containers and the Dependency Injection pattern

마틴 파울러는 IoC 컨테이너와 Dependency Injection Pattern에 대한 글로써 IoC에 대한 개념을 잘 보여주고 있다.
IoC 컨테이너와 의존성 삽입 패턴 글을 보면 윗글의 번역과 함께
글쓴이의 상세한 설명을 덧붙여서 이러한 개념을 처음 접하는 이들에게 많은 도움을 주고 있다.
깔끔하게 번역된 IoC 콘테이너와 디펜던시 인젝션 패턴 문서를 참고해보는 것도 좋겠다.

2.3 IoC컨테이너의 분류

IoC 개념은 글을 쓰는 저자마다 다양한 방식으로 분류하고 있다. Spring 프레임워크 워크북에 따르면 다음과 같이 분류하고 있다.

2.3.1 DL(Dependency Lookup) 혹은 DP(Dependency Pull)

Dependency Lookup은 저장소에 저장되어 있는 빈(Bean)에 접근하기 위하여 개발자들이 컨테이너에서 제공하는 API를 이용하여
사용하고자 하는 빈(Bean)을 Lookup하는 것을 말한다. Pro Spring책에서는 Dependency Pull이라고 정의했다.

아래 코드는 JNDI에 저장되어 있는 UserService EJB를 Lookup하는 예제이다.

package net.javajigi.user.client;

public class UserServiceDelegate {
    protected static final Log logger = LogFactory
            .getLog(UserServiceDelegate.class);

    private static final Class homeClazz = UserServiceHome.class;

    private UserService userService = null;

    public UserServiceDelegate() {
        ServiceLocator serviceLocator = ServiceLocator.getInstance();
        UserServiceHome userServiceHome = (UserServiceHome) serviceLocator
                .getRemoteHome(UserServiceHome.JNDI_NAME, homeClazz);
        try {
            userService = userServiceHome.create();
        } catch (RemoteException e) {
..........추가 코드...........
        
        }
    }
..........추가 코드...........
}

아래는 Spring 프레임워크 기반하의 UserService 인스턴스를 WebApplicationContext에서 Lookup하고 있다.

package net.javajigi.user.spring;

public class UserServiceHelper {
    private static final String USERSERVICE_BEANID = "userService";

    public static UserService getUserService(ServletContext ctx) {
        WebApplicationContext wac = WebApplicationContextUtils
                .getRequiredWebApplicationContext(ctx);

        return (UserService) wac.getBean(USERSERVICE_BEANID);
    }
}

2.3.2 DI(Dependency Injection)

Dependency Injection은 Spring 프레임워크에서 지원하는 IoC의 형태이다.
DI는 클래스 사이의 의존관계를 빈 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해주는 것을 말한다.
개발자들은 빈 설정 파일(저장소 관리 파일)에 의존관계가 필요하다는 정보를 추가하면 된다.
Spring 프레임워크는 Setter Injection, Constructor Injection, Method Injection의 세가지 유형으로 나타난다.

  • Setter Injection
    Setter Injection은 클래스 사이의 의존관계를 연결시키기 위하여 setter 메소드를 이용하는 방법이다. 예제코드는 다음과 같다.
package net.javajigi.user.service;

public class UserServiceImpl implements UserService, InitializingBean {

	private UserDAO userDAO;

	public void setUserDAO(UserDAO newUserDAO) {
		this.userDAO = newUserDAO;
	}

	public int addUser(User user) throws ExistedUserException {
		if (userDAO.existedUser(user.getUserId())) {
			throw new ExistedUserException(context.getMessage(
					"user.existed.exception",
					new Object[] { user.getUserId() }, null));
		}

		int	result = userDAO.insert(user);

		return result;
	}
..........추가 코드...........
}

위 코드를 보면 퍼시스턴스 계층과의 통신을 위해 UserDAO 인터페이스와 의존관계가 형성된다.
소스코드 어디에도 UserDAO 구현 클래스의 정보와 인스턴스가 생성되는 곳이 없다.
그러나 위 클래스는 UserDAO 인스턴스가 존재한다는 가정하에 개발을 진행하고 있는데 이것이 가능한 이유는
아래와 같은 Spring 프레임워크의 빈 설정파일에 있다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
	..........추가 설정...........
	<bean id="userDAO" class="net.javajigi.user.dao.MySQLUserDAO">

	<bean id="userService" class="net.javajigi.user.service.UserServiceImpl">
		<property name="userDAO">
			<ref local="userDAO"/>
		</property>
	</bean>
</beans>
  • Constructor Injection
    Constructor Injection은 생성자를 이용하여 의존관계를 연결시킨다.
    위 소스를 Constructor Injection으로 변경하면 다음과 같다.
package net.javajigi.user.service;

public class UserServiceImpl implements UserService, InitializingBean {

	private UserDAO userDAO;

	public UserServiceImpl (UserDAO newUserDAO) {
		this.UserDAO = newUserDAO;
	}

	public int addUser(User user) throws ExistedUserException {
		if (userDAO.existedUser(user.getUserId())) {
			throw new ExistedUserException(context.getMessage(
					"user.existed.exception",
					new Object[] { user.getUserId() }, null));
		}

		int	result = userDAO.insert(user);

		return result;
	}
..........추가 코드...........
}

빈 설정파일은 아래와 같이 작성된다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
	..........추가 설정...........
	<bean id="userDAO" class="net.javajigi.user.dao.MySQLUserDAO">

	<bean id="userService" class="net.javajigi.user.service.UserServiceImpl">
		<constructor-arg>
			<ref local="userDAO"/>
		</constructor-arg>
	</bean>
</beans>
  • Method Injection
    Method Injection은 Setter Injection과 Constructor Injection이 가지고 있는 한계점을 극복하기 위하여
    Spring 프레임워크 1.1 버전에서 새롭게 지원하고 있는 DI의 한 종류이다.
    Singleton 인스턴스와 Non Singleton 인스턴스의 의존관계를 연결할 필요가 있을때 사용한다.
    Method Injection 예제 문서를 참고하자.

3. Spring Framework 내의 IoC 개념

3.1 Spring Container

3.2 Spring 프레임워크의 초기화

  • BeanFactory와 ApplicationContext
    Spring 프레임워크는 빈 설정 파일에 정의되어 있는 설정정보를 초기화하고 초기화된 빈에 접근하기 위해
    BeanFactory와 그 하위에 ApplicationContext 인터페이스를 가지고 있다.
    아래 다이어그램을 참고하기 바란다.

BeanFactory UML Diagram

ApplicationContext UML Diagram

ApplicationContext의 실제 소스는 다음과 같다.

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.io.support.ResourcePatternResolver;

/** 
 * Central interface to provide configuration for an application.
 * This is read-only while the application is running, but may be
 * reloaded if the implementation supports this.
 *
 * <p>An ApplicationContext provides:
 * <ul>
 * <li>Bean factory methods, inherited from ListableBeanFactory.
 * This avoids the need for applications to use singletons.
 * <li>The ability to resolve messages, supporting internationalization.
 * Inherited from the MessageSource interface.
 * <li>The ability to load file resources in a generic fashion.
 * Inherited from the ResourceLoader interface.
 * <li>The ability to publish events. Implementations must provide a means
 * of registering event listeners.
 * <li>Inheritance from a parent context. Definitions in a descendant context
 * will always take priority. This means, for example, that a single parent
 * context can be used by an entire web application, while each servlet has
 * its own child context that is independent of that of any other servlet.
 * </ul>
 *
 * <p>In addition to standard bean factory lifecycle capabilities,
 * ApplicationContext implementations need to detect ApplicationContextAware
 * beans and invoke the setApplicationContext method accordingly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see ApplicationContextAware#setApplicationContext
 */
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
	
	ApplicationContext getParent();
	
	String getDisplayName();

	long getStartupDate();

	void publishEvent(ApplicationEvent event);

}

소스의 중간에 javadoc를 위한 주석을 보면 ApplicationContext는 BeanFactory가 가지고 있는 기능 외에 추가적인 기능을 지원하고 있다는 것을 알 수 있다.

애플리케이션을 개발할 때에는 거의 모든 경우에 ApplicationContext를 사용하면 된다.
두 인터페이스의 API에 직접 접근 하는 경우도 거의 없다.
ApplicationContext를 유용하게 사용할 수있는 경우는 테스트 프로그램을 작성할 때이다.
다음 예에서 그 활용법을 확인할 수 있다.

package net.javajigi.user.dao;

..........추가 코드...........

public class MySQLUserDAOTest extends TestCase {

	private ApplicationContext ctx;

	private UserDAO userDAO;

	protected void setUp() throws Exception {
		String[] paths = { "/WEB-INF/applicationContext.xml" };
		ctx = new ClassPathXmlApplicationContext(paths);

		userDAO = (UserDAO) ctx.getBean("userDAO");
	}
..........추가 코드...........
}

3.3 Spring 프레임워크에서 빈의 생명주기 관리

  • 빈의 초기화 과정
    • Spring 프레임워크의 인터페이스를 상속하여 초기화
      아래 소스코드를 보면 InitializingBean을 상속하여 afterPropertiesSet() 메소드를 구현하고 있다.
      여기에서는 빈 설정파일에 별도의 추가작업이 없다.
package net.javajigi.user.service;
..........추가 코드...........
public class UserServiceImpl implements UserService, InitializingBean {
	protected final Log logger = LogFactory.getLog(getClass());

	private UserDAO userDAO;

	private ExceptionMailSender mailSender;

	public void afterPropertiesSet() throws Exception {
		if (userDAO == null) {
			throw new Exception(context.getMessage("instance.not.init",
					new Object[] { "UserDAO" }, null));
		}

		if (mailSender == null) {
			throw new Exception(context.getMessage("instance.not.init",
					new Object[] { "ExceptionMailSender" }, null));
		}
	}

	public void setUserDAO(UserDAO newUserDAO) {
		this.userDAO = newUserDAO;
	}

	public void setMailSender(ExceptionMailSender mailSender) {
		this.mailSender = mailSender;
	}
..........추가 코드...........
}
    • 초기화를 위한 메소드를 빈 설정파일에 추가하여 초기화
package net.javajigi.user.service;
..........추가 코드...........
public class UserServiceImpl implements UserService {
	protected final Log logger = LogFactory.getLog(getClass());

	private UserDAO userDAO;

	private ExceptionMailSender mailSender;

	public void init() throws Exception {
		if (userDAO == null) {
			throw new Exception(context.getMessage("instance.not.init",
					new Object[] { "UserDAO" }, null));
		}

		if (mailSender == null) {
			throw new Exception(context.getMessage("instance.not.init",
					new Object[] { "ExceptionMailSender" }, null));
		}
	}

	public void setUserDAO(UserDAO newUserDAO) {
		this.userDAO = newUserDAO;
	}

	public void setMailSender(ExceptionMailSender mailSender) {
		this.mailSender = mailSender;
	}
..........추가 코드...........
}
<beans>
	..........추가 설정...........
	<bean id="userService" class="net.javajigi.user.service.UserServiceImpl" init-method="init">
		<property name="userDAO">
			<ref local="userDAO"/>
		</property>
		<property name="mailSender">
			<ref local="mailSender"/>
		</property>
	</bean>
	..........추가 설정...........
</beans>
  • 빈의 소멸 과정
    • Spring 프레임워크의 DisposableBean을 상속하여 destroy() 메소드 구현
    • 빈 설정파일에 destory-method 속성 추가
      <beans>
      	..........추가 설정...........
      	<bean id="userService" class="net.javajigi.user.service.UserServiceImpl" init-method="init">
      		<property name="userDAO">
      			<ref local="userDAO"/>
      		</property>
      		<property name="mailSender">
      			<ref local="mailSender"/>
      		</property>
      	</bean>
      	..........추가 설정...........
      </beans>
      
    • 3.4 Spring 프레임워크에서 빈의 관리방식
      Spring 프레임워크 기반 하에서 관리되는 POJO 빈들은 디폴트로 Singleton 방식으로 관리된다.
      Non Singleton 으로 빈을 관리하기 위해서는 빈 설정파일에 추가적인 설정을 해야 한다.
      빈 설정파일에 다음과 같이 빈을 정의하면 MySQLUserDAO 인스턴스는 Singleton 방식으로 관리된다.
      <bean id="userDAO" class="net.javajigi.user.dao.MySQLUserDAO" />

Non Singleton 방식으로 관리하고자 한다면 빈 속성중에 "singleton" 속성을 false로 설정하면 된다.
<bean id="userDAO" class="net.javajigi.user.dao.MySQLUserDAO" singleton="false" />

Singleton과 Non-Singleton을 설정하기 위한 예제 를 참고하도록 하자.

4. 토론꺼리

  • Spring 프레임워크를 어디다 써야할까?^^
  • Spring 프레임워크를 기반으로 하는 애플리케이션의 규모는 어느 정도일까?
  • Spring 프레임워크를 사용시 개발 제안시 Customer을 이해시키기 위한 방법은?
  • Spring 프레임워크를 사용시 개발자에 대한 교육방안은?
  • Spring 프레임워크를 사용하여 개발시 선행되어야하는 전제조건은?
  • Spring 프레임워크의 IoC 기반으로 애플리케이션을 개발할 때의 장점은?
  • IoC 기반으로 애플리케이션을 개발할 때의 테스트 용이성은 어떠할까?
  • Spring 프레임워크를 이용할 경우 Interface Oriented Programming이 가능한데 이 방식의 강점은? 굳이 인터페이스를 사용할 필요가 있을가?

5. 맺음말

  • 이상으로 IoC의 개념과 Spring IoC의 모습을 간략하게 살펴보았다.
    Spring 프레임워크가 처음 접하기에는 참 어렵게 느껴졌지만 자꾸 보면 볼수록 그 유연하면서도 막강한 기능에 빠져들게 된다.
    기본적인 IoC 개념부터가 차근차근 이해해 나가면 우리는 든든한 지원군 하나를 더 얻는 셈이 될 것이다.
    이 환상적인 계절인 Spring에 *'서비스의 설정과 사용을 어떻게 분리할 것인가'*에 대한 많은 답을 주는 Spring을 느껴보자..^^

이 문서는 지속적으로 업데이트를 계속할 예정이고 앞으로는 예제중심의 문서를 계속해서 추가해나가고자 한다.

참고문헌

문서에 대하여

최초작성자 : 강현수
최초작성일 : 2005년 4월 3일
버전 : 0.1
문서이력 :

  • 2005년 4월 7일 강현수 Version 0.1 : 1차 문서 작성 완료
  • 2005년 4월 3일 강현수 Version 0.1 : 기본 목록 작성
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. 4월 04, 2006

    남연숙 says:

    현수오빠 화이팅^^ 좋은 세미나 기대할게요

    현수오빠 화이팅^^ 좋은 세미나 기대할게요

  2. 4월 04, 2006

    자바지기 says:

    형 근무시간에 일 안하고 문서작업 하셔도 되요..? 근무시간에는 열심히 일해야지..ㅋㅋ 좋은 문서와 세미나 기대하고 있겠습니다.

    형 근무시간에 일 안하고 문서작업 하셔도 되요..?
    근무시간에는 열심히 일해야지..ㅋㅋ

    좋은 문서와 세미나 기대하고 있겠습니다.

  3. 4월 04, 2006

    안용상 says:

    형..열심이시네요. 은근히 기대되는걸요.. 수고하세요.

    형..열심이시네요.
    은근히 기대되는걸요.. 수고하세요.

  4. 4월 06, 2006

    김형준 says:

    Spring은 저도 기대가 많이 됩니다. 좋은 내용 부탁드립니다. 홧팅

    Spring은 저도 기대가 많이 됩니다.
    좋은 내용 부탁드립니다. 홧팅

  5. 4월 06, 2006

    yunbaly@gmail.com says:

    아!! 점점 더 부담이 된다..ㅡ.ㅡ;;

    아!! 점점 더 부담이 된다..ㅡ.ㅡ;;

  6. 4월 06, 2006

    고덕성 says:

    음.. 부담을 좀 더 줘보자.. 내일 통통!! 튀는 Spring의 실체를 파악해 볼 수 있는 훌륭한 세미나가 되겠지......^^ 고생이 많네...

    음.. 부담을 좀 더 줘보자..
    내일 통통!! 튀는 Spring의 실체를 파악해 볼 수 있는 훌륭한 세미나가 되겠지......^^

    고생이 많네...ㅎㅎ

  7. 4월 07, 2006

    자바지기 says:

    앗..제가 어제 토론꺼리 3개 올렸는데 날라갔다.. 어디로 간걸까.?

    앗..제가 어제 토론꺼리 3개 올렸는데 날라갔다..
    어디로 간걸까.?

  8. 4월 07, 2006

    장회수 says:

    오늘 못갈것 같은데.. 토론에 대한 의견들이 여기에 올라왔으면 하는 간곡한 바램이 있습니다.~~~~ ^^

    오늘 못갈것 같은데.. 토론에 대한 의견들이 여기에 올라왔으면 하는 간곡한 바램이 있습니다.~~~~ ^^