Spring Boot를 사용한 웹 MVC 애플리케이션 예제.

 

먼저, Spring Boot에서는 템플릿 엔진으로 Thymeleaf를 사용하고 있다.

 

본 예제는 DB에서 메뉴 정보를 조회하여 메뉴 정보를 가지고 메뉴 페이지로 이동하는 URL을 가정한 예제이다.

 

DB는 상황마다 다를 수 있으므로 DB에 관한 내용은 포함되어 있지 않다.

 

애플리케이션의 동작 순서

 

1. /menu URL을 입력하게 되면, MenuController의 goToMenu함수가 실행된다.

2. Spring Framework에 의해 생성된 MenuService의 getMenu함수가 실행된다.

3. 메뉴 정보를 조회한다.(현재는 하드코딩되어 있으며, DB 조회하는 내용으로 변경이 필요하다.)

4. 메뉴정보를 Model에 담는다.

5. Html에서 Thymeleaf을 사용하여 Object를 화면에 표시한다.

 

<!-- pom.xml의 dependencies -->
<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- spring-boot와 함께 thymeleaf도 포함 되어 있어야 한다. -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
</dependencies>
//DemoMvcApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoMvcApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoMvcApplication.class, args);
	}
}
//MenuController.java
package com.example.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.dto.MenuDTO;
import com.example.demo.service.MenuService;

@Controller
public class MenuController {

	@Autowired
	private MenuService menuService;
	
	@RequestMapping("/menu")
	public String goToMenu(Model model) {
		List<MenuDTO> menuList = menuService.getMenu();
		model.addAttribute("menuList", menuList);
		return "menu";
	}
}
//MenuService.java
package com.example.demo.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.example.demo.dto.MenuDTO;

@Service
public class MenuService {
	public List<MenuDTO> getMenu() {
		//DB에서 메뉴 데이터를 조회하는 내용으로 변경이 필요한 소스 시작
		List<MenuDTO> menuList = new ArrayList<MenuDTO>();
		MenuDTO menuDto = new MenuDTO();
		menuDto.setMenuId(1);
		menuDto.setParentMenuId(0);
		menuDto.setMenuName("Top");
		menuList.add(menuDto);
		menuDto = new MenuDTO();
		menuDto.setMenuId(2);
		menuDto.setParentMenuId(1);
		menuDto.setMenuName("First Menu");
		menuList.add(menuDto);
		menuDto = new MenuDTO();
		menuDto.setMenuId(3);
		menuDto.setParentMenuId(1);
		menuDto.setMenuName("Second Menu");
		menuList.add(menuDto);
		//DB에서 메뉴 데이터를 조회하는 내용으로 변경이 필요한 소스 끝
		return menuList;
	}
}
//MenuDTO.java
package com.example.demo.dto;

import java.util.Date;

import lombok.Data;

@Data
public class MenuDTO {
	private int menuId;
	private int parentMenuId;
	private String menuName;
	private Date regDate;
	private String regId;
	private Date modDate;
	private String modId;
}
<!-- /src/main/resources/templates/menu.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Menu</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div>Menu Page</div>
<ul th:each="menu: ${menuList}">
      <li th:text="${menu.menuName}"></li>
</ul>
</body>
</html>

 

Spring Framework에서는 AOP 사용을 위하여 XML방식도 지원을 한다.

        

AOP라는 네임스페이스 태그를 사용하면 된다.

 

XML 방식 사용 시, XML선언에 하단 내용을 추가해주여야 한다.

(기존 내용에서 xmlns:aop, xsi:schemaLocation의 aop 부분이 추가되었다.)

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
		https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

XML을 사용하여 AOP 전체 예제 내용

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<aop:config>
		<aop:aspect id="aspect" ref="schemaBasedCls">
		
			<aop:pointcut id="executionTest" expression="execution(* com.example.aop.PrintCls.*(..))"/>
			
			<aop:before 
				pointcut-ref="executionTest" 
				method="beforeTest"/>
			<aop:after-returning
        		pointcut-ref="executionTest"
       			returning="retVal"
        		method="executionAndReturnValTest"/>
		  	<aop:after-throwing
		        pointcut-ref="executionTest"
		        throwing="ex"
		        method="executionAndAfterThrowingValTest"/>
		  	<aop:after
        		pointcut-ref="executionTest"
        		method="executionAndAfterTest"/>
			<aop:around
        		pointcut-ref="executionTest"
        		method="executionAndAroundTest"/>
		</aop:aspect>
	</aop:config>
	
	<bean id="schemaBasedCls" class="com.example.aop.SchemaBasedCls"></bean>
	<bean id="printCls" class="com.example.aop.PrintCls"></bean>
</beans>
package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ImportResource;

import com.example.aop.PrintCls;

@SpringBootApplication
@ImportResource("classpath:aop.xml")
public class DemoSchemabasedApplication {
	private static final Logger logger = LoggerFactory.getLogger(DemoSchemabasedApplication.class);	
	
	public static void main(String[] args) {
		ConfigurableApplicationContext run = SpringApplication.run(DemoSchemabasedApplication.class, args);
		
		PrintCls bean = run.getBean(PrintCls.class);

		logger.info("--------------    bean method start	--------------");
		bean.printExample();
		logger.info("--------------    bean method end	--------------");
		
		logger.info("--------------    bean return method start    --------------");
		bean.returnStr();
		logger.info("--------------    bean return method end    --------------");
		
		logger.info("--------------    bean throw method start    --------------");
		try {
			bean.returnThrow();
		} catch (Exception e) {
		}
		logger.info("--------------    bean throw method end    --------------");
	}
}
package com.example.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrintCls {

	private static final Logger logger = LoggerFactory.getLogger(PrintCls.class);
	
	public void printExample() {
		logger.info("*** printExample");
	}
	
	public String returnStr() {
		String value = "testReturning";
		logger.info("*** print returning Str" + value );
		return value;
	}
	
	public void returnThrow() throws Exception {
		String msg = "execption test";
		logger.info("*** print throw " + msg);
		throw new Exception(msg);	
	}
}
package com.example.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SchemaBasedCls {
	private static final Logger logger = LoggerFactory.getLogger(SchemaBasedCls.class);
	
	//--before--
	public void beforeTest() {
		logger.info("before start");
		logger.info("before end");
	}
	//--before--
	
	//--AfterReturning--
	public void executionAndReturnValTest(String retVal) {
		logger.info("execution and afterReturning Value start");
		logger.info("Value :" + retVal);
		logger.info("execution and afterReturning Value end");
	}
	//--AfterReturning--
	
	//--AfterThrowing--
	public void executionAndAfterThrowingValTest(Exception ex) {
		logger.info("execution and AfterThrowing Value start");
		logger.error(ex.getMessage());
		logger.info("execution and AfterThorwing Value end");
	}
	//--AfterThrowing--
	
	//--After--
	public void executionAndAfterTest() {
		logger.info("execution and After start");
		logger.info("execution and AFter end");
	}
	//--After--
	
	//--Around--
	public void executionAndAroundTest(ProceedingJoinPoint pjp) {
		logger.info("execution and around start");
		logger.info(pjp.getSignature().getName() + " Before Method Execution");
		try {
			pjp.proceed();
		} catch (Throwable e) {
			logger.info(pjp.getSignature().getName() + "Throw Method Execution");
		} finally {
			// Do Something useful, If you have
		}
		logger.info(pjp.getSignature().getName() + " After Method Execution");
		logger.info("execution and around end");
	}
	//--Around
}

PointCut

- execution : 지정한 메서드가 일치하는 곳에서 Aspect 적용.

- within : 특정 타입에 대해서 Aspect 적용.

- bean : 스프링 빈 이름을 사용하여 빈의 메서드에 적용.

- 연산자를 통한 PointCut ( 예시 : @PointCut("executionTest() && withinTest()") )

 

Advice

-Before : 대상 메서드가 호출되기 전에 Advice 수행

-AfterReturning : 대상 메서드가 성공적으로 완료된 후에 Adivce 수행

-AfterThrowing : 대상 메서드가 예외를 던진 후에 Advice 수행

-After : 대상 메서드의 결과에 상관없이 Advice 수행 ( finally와 같이 최종적으로 수행 )

-Around : 대상 메서드를 감싸서 대상 메서드 호출 전과 후에 Advice 수행

 

Introductions

-DeclarParents : 인터페이스로 구현된 클래스를 대상 메서드의 부모로 추가할 수 있는 기능.

 

 

<!--pom.xml-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
	<version>2.4.2</version>
</dependency>
<!--service.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
     <bean id="printCls" class="com.example.aop.PrintCls"></bean>
     <bean id="aspectJTest" class="com.example.aop.AspectJTest"></bean>
</beans>
//DemoApplication.java

@SpringBootApplication
@EnableAspectJAutoProxy
@ImportResource("classpath:service.xml")
public class DemoApplication {
	private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);

	public static void main(String[] args) {
		ApplicationContext run = SpringApplication.run(DemoApplication.class, args);
		
		PrintCls bean = run.getBean(PrintCls.class);

		logger.info("--------------    bean method start	--------------");
		bean.printExample();
		logger.info("--------------    bean method end	--------------");
		
		logger.info("--------------    bean return method start    --------------");
		bean.returnStr();
		logger.info("--------------    bean return method end    --------------");
		
		logger.info("--------------    bean throw method start    --------------");
		try {
			bean.returnThrow();
		} catch (Exception e) {
		}
		logger.info("--------------    bean throw method end    --------------");
	}
}
//PrintCls.java

package com.example.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrintCls{
	private static final Logger logger = LoggerFactory.getLogger(PrintCls.class);
	
	public void printExample() {
		logger.info("*** printExample");
	}
	
	public String returnStr() {
		String value = "testReturning";
		logger.info("*** print returning Str" + value );
		return value;
	}
	
	public void returnThrow() throws Exception {
		String msg = "execption test";
		logger.info("*** print throw " + msg);
		throw new Exception(msg);	
	}

}
//DelcarInterface.java

package com.example.aop;

public interface DelcarInterface {
	public void declarInterfaceTest();
}
//DeclarInterfaceImpl.java

package com.example.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DeclarInterfaceImpl implements DelcarInterface {
	private static final Logger logger = LoggerFactory.getLogger(DeclarInterfaceImpl.class);
	
	@Override
	public void declarInterfaceTest() {
		logger.info("/////////////// declarInterfaceTest ///////////////////");
	}
}
//AspectJTest.java

package com.example.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class AspectJTest {

	private static final Logger logger = LoggerFactory.getLogger(AspectJTest.class);

	//--PointCut--
	@Pointcut("execution(* com.example.aop.PrintCls.*(..))")
	public void executionTest() {
		logger.info("executeTest start");
		logger.info("executeTest end");
	}
	
	@Pointcut("within(com.example.aop.PrintCls)")
	public void withinTest() {
		logger.info("withinTest start");
		logger.info("withinTest end");
	}
	//--PointCut--
	
	//--Before--
	@Before("execution(* com.example.aop.PrintCls.*(..))")
	public void beforeTest() {
		logger.info("before start");
		logger.info("before end");
	}
	
	@Before("executionTest()")
	public void executionAndBefore() {
		logger.info("execution and Before start");
		logger.info("execution and Before end");
	}
	
	@Before("withinTest()")
	public void withinAndBefore() {
		logger.info("within and Before start");
		logger.info("within and Before end");
	}
	//--Before--
	
	
	//--AfterReturning--
	@AfterReturning("execution(* com.example.aop.PrintCls.*(..))")
	public void afterReturningTest() {
		logger.info("afterReturningTest start");
		logger.info("afterReturningTest end");
	}
	
	@AfterReturning("executionTest()")
	public void executionAndAfterReturning() {
		logger.info("execution and afterReturning start");
		logger.info("execution and afterReturning end");
	}
	
	@AfterReturning("withinTest()")
	public void withinAndAfterReturningTest() {
		logger.info("within and afterReturning start");
		logger.info("within and afterReturning end");
	}

	@AfterReturning(
			pointcut="executionTest()",
			returning="retVal")
	public void executionAndReturnValTest(String retVal) {
		logger.info("execution and afterReturning Value start");
		logger.info("Value :" + retVal);
		logger.info("execution and afterReturning Value end");
	}
	
	@AfterReturning(
			pointcut="withinTest()",
			returning="retVal")
	public void withinAndReturnValTest(String retVal) {
		logger.info("within and afterReturning Value start");
		logger.info("Value :" + retVal);
		logger.info("within and afterReturning Value end");
	}
	//--AfterReturning--
	
	//--AfterThrowing--
	 @AfterThrowing("executionTest()")
	 public void executionAndAfterThrowingTest() {
		 logger.info("execution and AfterThrowing start");
		 logger.info("execution and AfterThrowing end");
	 }
	 
	 @AfterThrowing("withinTest()")
	 public void withinAndAfterThrowingTest() {
		 logger.info("within and AfterThrowing start");
		 logger.info("within and AfterThrowing end");
	 }
	 
	 @AfterThrowing(
		        pointcut="executionTest()",
		        throwing="ex")
	 public void executionAndAfterThrowingValTest(Exception ex) {
		 logger.info("execution and AfterThrowing Value start");
		 logger.error(ex.getMessage());
		 logger.info("execution and AfterThorwing Value end");
	 }
	 
	 
	 @AfterThrowing(
		        pointcut="executionTest()",
		        throwing="ex")
	 public void withinAndAfterThrowingValTest(Exception ex) {
		 logger.info("execution and AfterThrowing Value start");
		 logger.error(ex.getMessage());
		 logger.info("execution and AfterThorwing Value end");
	 }
	 //--AfterThrowing--
	 
	 
	 //--After--
	 @After("executionTest()")
	 public void executionAndAfterTest() {
		 logger.info("execution and After start");
		 logger.info("execution and AFter end");
	 }
	 
	 @After("withinTest()")
	 public void withinAndAfterTest() {
		 logger.info("within and after start");
		 logger.info("within and after end");
	 }
	 //--After
	 
	 //--Around
	 @Around("executionTest()")
	 public void executionAndAroundTest(ProceedingJoinPoint  pjp){
		 logger.info("execution and around start");
		 logger.info(pjp.getSignature().getName() + " Before Method Execution");
		 try {
			 pjp.proceed();
		 } catch (Throwable e) {
			 logger.info(pjp.getSignature().getName() + "Throw Method Execution");
		 } finally {
			 // Do Something useful, If you have
		 }
		 logger.info(pjp.getSignature().getName() + " After Method Execution");
		 logger.info("execution and around end");
	 }
	 
	 @Around("withinTest()")
	 public void withinAndAroundTest(ProceedingJoinPoint  pjp) {
		 logger.info("within and around start");
		 logger.info(pjp.getSignature().getName() + " Before Method Execution");
		 try {
			 pjp.proceed();
		 } catch(Throwable e) {
			 logger.info(pjp.getSignature().getName() + "Throw Method Execution");
		 } finally {
			 // Do Something useful, If you have
		 }
		logger.info(pjp.getSignature().getName() + " After Method Execution");

		 logger.info("within and around end");
	 }
	 //--Around--
	 
	 
	 
	 //--Introductions ( DeclarParents )--
	 @DeclareParents(value="com.example.aop.PrintCls", defaultImpl=DeclarInterfaceImpl.class)
	 public static DelcarInterface mixin;

	 @Before("executionTest() && this(dInterface)")
	 public void declarTest(DelcarInterface dInterface) {
		 dInterface.declarInterfaceTest();
	 }
	 //--Introductions ( DeclarParents )--
}

AOP

- 기본적인 개념은 공통 관심 사항을 구현한 코드를 핵심로직을 구현한 코드 안에 삽입한다는 것.

   

AOP 용어

- Aspect : 공통 코드 ( ex : 트랙잭션, 로깅 )

- Advice : 공통코드를 적용하는 시점. ( 특정 메소드 실행전, 특정 메소드 실행후 등등 )

- Joinpoint : Advice가 적용 가능한 지점. 

- Pointcut : JointPoint중에서 Aspect가 적용되는 위치.

- Introduction : 함수나 멤버변수를 추가적으로 선언하는 기능

- Weaving : 핵심로직에서 Aspect가 동작이 되는 것.

    Weaving 방식

    1. 컴파일 시

    2. 클래스 로딩시

    3. 런타임 시

 

Spring에서 제공되는 AOP.

- @AspectJ ( 어노테이션 방식 )

- Schema-based AOP ( XML 방식 )



시스템의 요구사항이나 설계에 따라 상황에 맞는 AOP를 사용하면 되지만, 

Schema-based AOP가 가진 단점을 이해해야 한다.

 

1. XML방식이기 때문에 완전한 캡슐화 되지는 않는다.

- 설정파일에 XML과 빈(Bean)에 대한 선언이 분할되어 사용되기 때문이다.

2. XML에 선언 된 포인트 컷을 결합 할 수 없습니다. 

@Pointcut("execution(* get*())")
public void propertyAccess() {}

@Pointcut("execution(org.xyz.Account+ *(..))")
public void operationReturningAnAccount() {}

@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {}
<aop:pointcut id="propertyAccess"
        expression="execution(* get*())"/>

<aop:pointcut id="operationReturningAnAccount"
        expression="execution(org.xyz.Account+ *(..))"/>

DI ( Dependency Injection )은 객체 간의 결합을 느슨하게 만들어주는 개념이다.



1. 객체간의 강한 결합 vs 느슨한 결합

2. 왜 느슨한 결합이 좋은 것 인가?

3. Spring에서 DI를 어떻게 사용하면 되는 것 인가?

 

1. 객체간의 강한 결합 vs 느슨한 결합

public interface BookDAO{}
public class MagazineDAO implements BookDAO{ String monthly = ""; }
public class NovelDAO implements BookDAO{}


public interface Book{}
public class MagazineServiceImpl implements Book{
    private BookDAO bookDAO;
    public MagazineServiceImpl() {
        //강한 결합
        //MagazineService 객체가 생성되는 시점에 
        //MagazineDAO를 생성하여 사용하게 된다.
        bookDAO = new MagazineDAO();
    }
    
}

public class NovelServiceImpl implements Book{
    private BookDAO bookDAO;
    public NovelServiceImpl(BookDAO bookDAO) {
        //느슨한 결합
        //NovelService 객체가 생성되는 시점에
        //BookDAO가 자동으로 주입되어 사용하게 된다.
        this.bookDAO = bookDAO;
    }
}

 

2. 왜 강한 결합을 사용하면 안 좋은 것인가?

강한 결합을 사용하는 것이 반드시 나쁜 것이 아니다.

작은 프로젝트에서 강한 결합을 사용하도 무방하다.

그러나, 대형 프로젝트에서는 많은 클래스 객체들이 생성되고, 관리되어야 하기 때문에 느슨한 결합을 사용하는 게 유리하다.

 

추가적으로, 코드의 재사용성이나 단위테스트에도 느슨한 결합이 강한 결합보다 유리하다. 

 

3. Spring에서 DI를 어떻게 사용하면 되는 것인가?

Spring에서는 대표적으로 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- NovelService 객체 정의 -->
    <bean id="NovelService" class="com.example.cls.NovelServiceImpl">  
        <!-- bookDAO라는 이름으로 NovelService Injection을 정의 -->
		<property name="bookDAO" ref="bookDAO"/>
    </bean>

    <!-- bookDAO 객체 정의 -->
    <bean id="bookDAO" class="com.example.cls.NovelDAO">
    </bean>
</beans>

 

Java의 Integer 클래스를 사용하여 진수 변환

 

Integer.toBinaryString(int value)  

- 10진수의 값(value)을 2진수로 변경

Integer.toOctalString(int value)

- 10진수의 값(value)을 8진수로 변경

Integer.toHexString(int value)

- 10진수의 값(value)을 16진수로 변경

Integer.parseInt(String s, int radix)

- 입력된 문자열 값(s)을 입력된 진수값(radix)에 맞게 변경

 

public void numberConvert(){
		
      //10진수 -> 2진수로 변환
      for( int i = 0 ; i < 20 ; i++ ){
            System.out.print(Integer.toBinaryString(i) + "\t");
      }
      System.out.println();
      
      //10진수 -> 8진수로 변환
      for ( int i = 0 ; i < 20 ; i++){
            System.out.print(Integer.toOctalString(i) + "\t");
      }
      System.out.println();
      
      //10진수 -> 16진수로 변환
      for ( int i = 0 ; i < 20 ; i++ ){
            System.out.print(Integer.toHexString(i) + "\t");
      }
      System.out.println();
      
      //2진수 -> 10진수로 변환
      System.out.println(Integer.parseInt("01",2));
      
      //8진수 -> 10진수로 변환
      System.out.println(Integer.parseInt("14",8));
      
      //16진수 -> 10진수로 변환
      System.out.println(Integer.parseInt("A",16));
}
//	0	1	10	11	100	101	110	111	1000	1001	1010	1011	1100	1101	1110	1111	10000	10001	10010	10011	
//	0	1	2	3	4	5	6	7	10	11	12	13	14	15	16	17	20	21	22	23	
//	0	1	2	3	4	5	6	7	8	9	a	b	c	d	e	f	10	11	12	13	
//	1
//	12
//	10

HTML5의 Form 속성 중 formaction 속성을 사용하게 되면 다중의 submit에 각각 다른 액션을 지정할 수 있다.

 

<form>
      <button type="submit" formmethod="GET"  formaction="/test"  value="테스트페이지로"/>
      <button type="submit" formmethod="POST" formaction="/check" value="체크페이지로"/>
</form>

 

브라우저별 지원 여부

크롬 익스플로러 파이어폭스 사파리 오페라
9.0 10 4.0 5.1 15.0

 

formaction에 입력하게되는 URL은 상대 경로(/test) or 절대 경로(http://www.[사이트명]. com/test) 모두 입력이 가능하다.

 

'프로그래밍 > HTML5' 카테고리의 다른 글

[HTML5] Audio & Video  (2) 2012.08.28
[HTML5] canvas  (0) 2012.08.27

Java 8 이상부터는 데이터 포맷 형식에서 연도를 표기하는 yyyy외에도 uuuu가 추가되어있다.

 

먼저 예시 코드와 출력물은 아래와 같다.

 

    private void checkYear(){
		LocalDateTime localDateTime = LocalDateTime.of(0, 12, 30, 0, 0);
		System.out.println("기원전에서 서기 날짜 출력");
		for ( int i = 0 ; i < 5 ; i++ ){
			String yyResult = localDateTime.format(DateTimeFormatter.ofPattern("G yyyy-MM-dd"));
			String uuResult = localDateTime.format(DateTimeFormatter.ofPattern("G uuuu-MM-dd"));
			System.out.println(yyResult + "\t" + uuResult);
			localDateTime = localDateTime.plusDays(1);
		}
		System.out.println("\n기원전, 서기 각각 100년에 대한 포맷 출력");
		LocalDateTime localDateTime2 = LocalDateTime.of(-100, 12, 30, 0, 0);
		LocalDateTime localDateTime3 = LocalDateTime.of(100, 12, 30, 0, 0);
		String yyResult2 = localDateTime2.format(DateTimeFormatter.ofPattern("G yyyy-MM-dd"));
		String uuResult2 = localDateTime2.format(DateTimeFormatter.ofPattern("G uuuu-MM-dd"));
		System.out.println(yyResult2 + "\t" + uuResult2);
		
		String yyResult3 = localDateTime3.format(DateTimeFormatter.ofPattern("G yyyy-MM-dd"));
		String uuResult3 = localDateTime3.format(DateTimeFormatter.ofPattern("G uuuu-MM-dd"));
		System.out.println(yyResult3 + "\t" + uuResult3);
	}
//	기원전에서 서기 날짜 출력
//	기원전 0001-12-30	기원전 0000-12-30
//	기원전 0001-12-31	기원전 0000-12-31
//	서기 0001-01-01	서기 0001-01-01
//	서기 0001-01-02	서기 0001-01-02
//	서기 0001-01-03	서기 0001-01-03
//
//	기원전, 서기 각각 100년에 대한 포맷 출력
//	기원전 0101-12-30	기원전 -0100-12-30
//	서기 0100-12-30	서기 0100-12-30

 

첫 번째 출력 결과로 확인할 수 있는 내용으로는 yyyy의 표기인 경우, 서기 1년 1월 1일 전일 은 기원전 0년 12월 31일이 아니라, 기원전 1년 12월 31일이 된다.

 

두 번째 출력 결과로 확인할 수 있는 내용으로는 기원전 100년을 표기하고 싶었지만, yyyy에서는 음수 처리와 함께 101년으로 변경되었다.

 

실제로 연도를 가지고 계산을 하거나 표기를 할 때는 uuuu 패턴을 사용하는 것이 안전할 수 있다고 볼 수 있다.

현재 날짜와 시간을 기준으로 특정한 날짜와 시간을 입력하여 함수를 실행하는 스크립트

 

 

function timerFunc(func, dateTime){
	//ex) timerFunc(function(){console.log('test');},'20210108144740');
    //시간은 24시간을 기준으로 입력하여야 합니다.
    var year = Number(dateTime.substring(0,4));
    var month = Number(dateTime.substring(4,6));
    var day = Number(dateTime.substring(6,8));
    var time = Number(dateTime.substring(8,10));
    var minute = Number(dateTime.substring(10,12));
    var second = Number(dateTime.substring(12,14));
     
    var oprDate = new Date(year, month-1, day, time, minute, second); //동작을 원하는 시간의 Date 객체를 생성합니다.
    var nowDate = new Date();                                         //현재 날짜와 시간을 확인
     
    var timer = oprDate.getTime() - nowDate.getTime(); //동작시간의 밀리세컨과 현재시간의 밀리세컨의 차이를 계산합니다.
    if(timer < 0){ //타이머가 0보다 작으면 함수를 종료합니다.
       return;
    }else{
       setTimeout(func, timer);
    }
}

 

메뉴코드 상위메뉴코드 메뉴명
1 0 MainPage
2 1 Second Menu 1
4 2 Third Menu 1
5 2 Third Menu 2
3 1 Second Menu 2


상단의 테이블 정보를 트리형식으로 표현하게 되면 아래와 같은 구조를 가지고 있음을 알 수 있다.

  • MainPage
    • Second Menu1
      • Third Menu 1
      • Third Menu 2
    • Second Menu2

 

최상위 메뉴인 MainPage를 기준으로 하위 페이지 내용들을 출력하기 위하여

1. HashMap을 사용하여 메뉴코드가 중복되지 않는 점을 이용

2. Parent-Child 관계를 확인 후, ChildrenList에 페이지 정보를 추가

3. 메뉴 정보 출력

위 순서대로 구성되어 있다.

 

public class MenuVO {

	private int menuSeq;
	private int menuUpSeq;
	private String menuNm;
	private int menuSno;
	
	private List<MenuVO> childrenList;

	
	public MenuVO(){
		super();
	}
	
	public MenuVO(MenuVO menuVO){
		this.menuSeq = menuVO.getMenuSeq();
		this.menuUpSeq = menuVO.getMenuUpSeq();
		this.menuNm = menuVO.getMenuNm();
		this.childrenList = new ArrayList<MenuVO>(); 
	}
	
	public int getMenuSeq() {
		return menuSeq;
	}

	public void setMenuSeq(int menuSeq) {
		this.menuSeq = menuSeq;
	}

	public int getMenuUpSeq() {
		return menuUpSeq;
	}

	public void setMenuUpSeq(int menuUpSeq) {
		this.menuUpSeq = menuUpSeq;
	}

	public String getMenuNm() {
		return menuNm;
	}

	public void setMenuNm(String menuNm) {
		this.menuNm = menuNm;
	}

	public int getMenuSno() {
		return menuSno;
	}

	public void setMenuSno(int menuSno) {
		this.menuSno = menuSno;
	}

	public List<MenuVO> getChildrenList() {
		return childrenList;
	}

	public void setChildrenList(List<MenuVO> childrenList) {
		this.childrenList = childrenList;
	}
	
	public void addChildren(MenuVO children){
		this.childrenList.add(children);
	}

	@Override
	public String toString() {
		return "MenuVO [menuSeq=" + menuSeq + ", menuUpSeq=" + menuUpSeq + ", menuNm=" + menuNm + ", menuSno=" + menuSno
				+ ", childrenList=" + childrenList + "]";
	}
}

 

 

public static void main(String[] args) {
	
	List<MenuVO> menuList = new ArrayList<MenuVO>();
	
	MenuVO topMenu = new MenuVO();
	topMenu.setMenuSeq(1);
	topMenu.setMenuUpSeq(0);
	topMenu.setMenuNm("Main Page");
	menuList.add(topMenu);

	MenuVO secondMenu1 = new MenuVO();
	secondMenu1.setMenuSeq(2);
	secondMenu1.setMenuUpSeq(1);
	secondMenu1.setMenuNm("secondMenu1 Page");
	menuList.add(secondMenu1);
	
	MenuVO thirdMenu1 = new MenuVO();
	thirdMenu1.setMenuSeq(4);
	thirdMenu1.setMenuUpSeq(2);
	thirdMenu1.setMenuNm("thirdMenu1 Page");
	menuList.add(thirdMenu1);

	MenuVO thirdMenu2 = new MenuVO();
	thirdMenu2.setMenuSeq(5);
	thirdMenu2.setMenuUpSeq(2);
	thirdMenu2.setMenuNm("thirdMenu2 Page");
	menuList.add(thirdMenu2);
	
	MenuVO secondMenu2 = new MenuVO();
	secondMenu2.setMenuSeq(3);
	secondMenu2.setMenuUpSeq(1);
	secondMenu2.setMenuNm("secondMenu2 Page");
	menuList.add(secondMenu2);
	
	
	
	
	HashMap<Integer, MenuVO> hm = new HashMap<Integer, MenuVO>();
	
	
    for(MenuVO p:menuList){
    	MenuVO mmdChild ;
        if(hm.containsKey(p.getMenuSeq())){
            mmdChild = hm.get(p.getMenuSeq());
        }
        else{
            mmdChild = new MenuVO(p);
            hm.put(p.getMenuSeq(),mmdChild);
        }           

        MenuVO mmdParent ;
        if(hm.containsKey(p.getMenuUpSeq())){
            mmdParent = hm.get(p.getMenuUpSeq());
        }
        else{
            mmdParent = new MenuVO(p);
            hm.put(p.getMenuUpSeq(),mmdParent);
        }
        mmdParent.setMenuSeq(p.getMenuUpSeq());
        mmdParent.addChildren(mmdChild);
        
        printHashMap(hm);
    }
  
    MenuVO menuVO = hm.get(1);
    printMenu(menuVO);
    	
}


public static void printMenu(MenuVO menuVO){
	
	System.out.println("#####         START          #####");
	System.out.println(menuVO.getMenuSeq() + " : " + menuVO.getMenuUpSeq() + " : " + menuVO.getMenuNm());
	
	List<MenuVO> tmp = menuVO.getChildrenList();
	
	if ( !tmp.isEmpty() ){
		for (MenuVO childeMenuVO : tmp) {
			printMenu(childeMenuVO);
		}
	}
	 System.out.println("@@@@@          END           @@@@@\n");
}

public static void printHashMap(HashMap<Integer, MenuVO> hm){
	Set<Integer> keySet = hm.keySet();
    System.out.println("$$$$$         START          $$$$$");
    for (Integer integer : keySet) {
		MenuVO menuVO = hm.get(integer);
		System.out.println(menuVO.toString());
	}
    System.out.println("$$$$$          END           $$$$$\n");
}

/*
결과
$$$$$         START          $$$$$
MenuVO [menuSeq=0, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[]]]]
MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[]]
$$$$$          END           $$$$$

$$$$$         START          $$$$$
MenuVO [menuSeq=0, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[]]]]]]
MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[]]]]
MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[]]
$$$$$          END           $$$$$

$$$$$         START          $$$$$
MenuVO [menuSeq=0, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]]]]]]]]
MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]]]]]]
MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]]]]
MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]]
$$$$$          END           $$$$$

$$$$$         START          $$$$$
MenuVO [menuSeq=0, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]], MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]]]]]]]
MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]], MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]]]]]
MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]], MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]]]
MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]]
MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]
$$$$$          END           $$$$$

$$$$$         START          $$$$$
MenuVO [menuSeq=0, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]], MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]]], MenuVO [menuSeq=3, menuUpSeq=1, menuNm=secondMenu2 Page, menuSno=0, childrenList=[]]]]]]
MenuVO [menuSeq=1, menuUpSeq=0, menuNm=Main Page, menuSno=0, childrenList=[MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]], MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]]], MenuVO [menuSeq=3, menuUpSeq=1, menuNm=secondMenu2 Page, menuSno=0, childrenList=[]]]]
MenuVO [menuSeq=2, menuUpSeq=1, menuNm=secondMenu1 Page, menuSno=0, childrenList=[MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]], MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]]]
MenuVO [menuSeq=3, menuUpSeq=1, menuNm=secondMenu2 Page, menuSno=0, childrenList=[]]
MenuVO [menuSeq=4, menuUpSeq=2, menuNm=thirdMenu1 Page, menuSno=0, childrenList=[]]
MenuVO [menuSeq=5, menuUpSeq=2, menuNm=thirdMenu2 Page, menuSno=0, childrenList=[]]
$$$$$          END           $$$$$

#####         START          #####
1 : 0 : Main Page
#####         START          #####
2 : 1 : secondMenu1 Page
#####         START          #####
4 : 2 : thirdMenu1 Page
@@@@@          END           @@@@@

#####         START          #####
5 : 2 : thirdMenu2 Page
@@@@@          END           @@@@@

@@@@@          END           @@@@@

#####         START          #####
3 : 1 : secondMenu2 Page
@@@@@          END           @@@@@

@@@@@          END           @@@@@
*/