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>

 

Model 파일 생성 시, Getter와 Setter 함수 생성이 불가피하다.

public class TestVO {
    private int id;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

변수가 2개만 해도 생성해야 함수가 4개나 된다. 

 

그러나, Model 객체에는 대부분 많은 변수 선언이 있기 때문에 많은 Getter, Setter 함수 생성에 시간을 허비하게 된다.

 

Lobmok을 사용하게 되면 어노테이션을 사용하는 것 만으로도 Getter, Setter 문제를 쉽게 해결할 수 있다.

 

Lombok 적용방법

1. jar 다운로드(https://projectlombok.org/download)

2. java -jar lombok.jar 실행

3. Eclipse 또는 STS 실행파일 선택 후, 설치

4. Eclipse 또는 STS 실행

5. 해당 프로젝트에 Lombok.jar 적용

6. Model 클래스에 어노테이션 적용

7. Outline창에 Getter,Setter 함수가 보이면 적용 완료.

 

@Data
public class TestVO {
	private int Id;
	private String name;
}

 

Lombok에서는 Getter,Setter외에도 생성자 함수와 널 설정 등 다양한 어노테이션이 제공되고 있다.

상세 내용은 공식 홈페이지를 참조하면 된다.(https://projectlombok.org/features/all)

'TOOL' 카테고리의 다른 글

Framework vs Library  (0) 2021.01.25
[PMD] Code Analyzers  (0) 2010.12.30
[Eclipse] 이클립스 단축키  (0) 2010.07.26

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
}

Framework와 Library의 공통점 : 공통된 문제점을 쉽게 해결하기 위하여 제공되는 것.

 

Spring Framework와 Log Library를 사용하여 차이점을 예시로 확인해보면 아래와 같다.

 

//Spring Framework의 @RequestMapping기능을 사용하여 /test URL을 매핑하였다.
//해당 URL로 접근하면 Spring Framework가 아래 메서드를 실행을 한다.
@RequestMapping("/test")
public void test() {
      //Log Library는 개발자에 의해서 실행하는 시점이 결정된다.
	logger.info("Logger Test");
}

 

제일 큰 차이점은, 애플리케이션에서 코드가 실행되는 주체와 관련이 있다.

 

Framework는 코드의 실행의 주체가 framework가 되어 소스코드를 실행시킨다.

 

Library는 코드의 실행 주체가 개발자에 의해서 정해지고 실행이 된다.

'TOOL' 카테고리의 다른 글

[Lombok] Lombok 사용법  (0) 2021.01.27
[PMD] Code Analyzers  (0) 2010.12.30
[Eclipse] 이클립스 단축키  (0) 2010.07.26

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

Oracle

SELECT * FROM TABS;
SELECT * FROM ALL_TAB_COLUMNS;

 

MySQL

SELECT * FROM INFORMATION_SCHEMA.TABLES;
SELECT * FROM INFORMATION_SCHEMA.COLUMNS;

 

두 DBMS에서 가장 큰 차이점은 Oracle의 경우, 'TABS','ALL_TAB_COLUMNS'와 같은 이름으로 바로 조회가 가능하지만

MySQL에서는 INFORMATION_SCHEMA라는 DB명과 그에 맞는 이름을 사용하여 조회를 해야 한다.

Oracle에서는 문자열은 ||을 사용하여 합치면 된다.

 

예시 

select ('2021-01-01' || ' ~ ' || '2021-01-02') as resultDate from dual;

 

MySQL은 CONCAT함수를 사용하여 합쳐야 한다

 

예시

select concat('2021-01-01','2021-01-02') as resultDate from dual;

 

추가적으로 MySQL은 CONCAT_WS([구분자], [인자값1], [인자값2], [인자값3],...)의 함수도 존재한다.

 

예시

select concat_ws(' ','서울특별시','중구','세종대로') as result from dual;

 

 

Oracle에서도 CONCAT함수를 제공한다. 그러나 인자값을 2개만 쓸 수 있으므로 불편한 상황이 발생한다.

 

예를 들면, 기존 SQL에 내용을 추가하게 될 때, 아래 예시 같이 쿼리의 가독성이 떨어지게 된다.

 

따라서, 몇 번의 추가적인 내용이 더 생기게 된다면 쿼리가 무엇을 합치려는지 쉽게 알 수가 없기 때문에 CONCAT함수보다는 ||의 사용을 더 선호한다.

 

예시

SELECT CONCAT('서울시','중구') FROM DUAL;
SELECT CONCAT(CONCAT('서울시','중구'),'세종대로') FROM DUAL;