<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<html>
<head>
<title>test</title>
</head>
<body>
<form action="calcResult.jsp" method="post">
<input type="number" name="num1"/>
<input type="number" name="num2"/>
<button type="submit">send</button>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>calcResult</title>
</head>
<body>
<%
int num1= Integer.parseInt(request.getParameter("num1"));
int num2= Integer.parseInt(request.getParameter("num2"));
%>
결과 : <%= num1 + num2 %>
<%--원래 문자 타입인데 + 연산자가 있어서 자동으로 Integer.parseInt처리
웹 파라미터는 모두 문자열--%>
<h1>${param.num1}+${param.num2} = ${param.num1+param.num2}</h1>
<h1> sum : ${Integer.parseInt(param.num1) + Integer.parseInt(param.num2)}</h1>
</body>
</html>
calcResult.jsp
파일이 webapp
폴더의 바로 아래에 있을 경우:javaCopy code
req.getRequestDispatcher("/calcResult.jsp").forward(req, resp);
calcResult.jsp
파일이 webapp
폴더 아래의 calc
폴더 안에 있을 경우:javaCopy code
req.getRequestDispatcher("/calc/calcResult.jsp").forward(req, resp);
calcResult.jsp
파일이 WEB-INF
폴더 아래의 calc
폴더 안에 있을 경우:javaCopy code
req.getRequestDispatcher("/WEB-INF/calc/calcResult.jsp").forward(req, resp);
Maven Repository: Search/Browse/Explore
package com.multicampus.dao;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mariadb.jdbc.Driver;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
public class ConnectTests {
//TDD, Test 어노테이션 이용
@Test
public void test1(){
int v1 = 10;
int v2 = 20;
// assertEquals 같다고 확신합니다라는 의미로 같아야 테스트 가능
Assertions.assertEquals(v1, v2);
}
@Test
public void testConntion() throws Exception {
Class.forName("org.mariadb.jdbc.Driver");
Connection connection = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/webdb",
"webuser",
"webuser"
);
Assertions.assertNotNull(connection);
connection.close();
}
}
💡 스프링은 비즈니스 로직과 데이터 액세스를 처리하고, 톰캣은 사용자 요청을 받아서 애플리케이션과 상호 작용해 동적인 웹 페이지를 제공해. 스프링은 서비스 레이어에서 비즈니스 로직을 구현하고, DAO 패턴을 이용해 데이터베이스와의 상호 작용을 캡슐화해. 이렇게 역할을 분리해서 서버를 운영하면 애플리케이션의 유지 보수성과 확장성을 향상시킬 수 있어.
웹 서버(WEB Server)와 서블릿 컨테이너(Servlet Container)의 기능을 제공
합니다. 사용자가 웹 브라우저를 통해 애플리케이션에 접속하면, 톰캣은 해당 요청을 받아서 처리합니다. 톰캣은 정적인 웹 페이지와 동적인 서블릿, JSP(JavaServer Pages) 등을 처리하여 사용자에게 응답을 제공합니다. 톰캣은 스프링의 비즈니스 로직과 DAO와는 직접적인 관련이 없으며, 단지 사용자의 요청을 받고 애플리케이션과 상호 작용하는 역할을 담당합니다.dependency
에 추가같은 버전
으로 설치하게 유의하자Maven Repository: org.springframework » spring-core
implementationgroup:'org.springframework',name:'spring-core',version:'5.3.19'
implementationgroup:'org.springframework',name:'spring-context',version:'5.3.19'
testImplementation group: 'org.springframework', name: 'spring-test', version: '5.3.19'
Lombok library
: dao만들 때 좀 더 편안하게 작업//Lombok library - dao만들 때 좀 더 편안하게 작업
compileOnlygroup:'org.projectlombok',name:'lombok',version:'1.18.24'
annotationProcessor'org.projectlombok:lombok:1.18.24'
testCompileOnly'org.projectlombok:lombok:1.18.24'
testAnnotationProcessor'org.projectlombok:lombok:1.18.24'
Log4j2
테스트 설정//@Log4j2 테스트 설정, logging : api
implementation group:'org.apache.logging.log4j', name:'log4j-core', version: '2.17.2'
implementation group:'org.apache.logging.log4j', name:'log4j-api', version: '2.17.2'
implementation group:'org.apache.logging.log4j', name:'log4j-slf4j-impl', version: '2.17.2'
jstl
: jsp을 이용해 화면 구성하니implementationgroup:'jstl',name:'jstl',version:'1.2'
bean
은 객체root-context
는 pojo를 컨테이너에게 등록하게 해주는 xml 파일 ⇒ spring_config로 만들기root-context.xml
에 bean 등록<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.multicampus.springex.sample.SampleDAO"></bean>
<bean class="com.multicampus.springex.sample.SampleService"></bean>
</beans>
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.multicampus.springex.sample.SampleDAO"></bean>
<bean class="com.multicampus.springex.sample.SampleService"></bean>
</beans>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--테스트 환경 레벨을 정함-->
<Configuration status="INFO">
<!--Appenders은 pattern으로 만들어서 console를 뿌리겠다 선언한 것-->
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<!--console에 위에 것을 등록-->
<Loggers>
<!--additivity="false"를 설정하면 해당 로거가 부모 로거의 설정을 상속받지 않는다. 즉, 독립적인 로그 레벨을 가지게 됨-->
<logger name="org.springframework" level="INFO" additivity="false">
<appender-ref ref="console"/>
</logger>
<logger name="com.multicampus" level="INFO" additivity="false">
<appender-ref ref="console"/>
</logger>
<Root level="info" additivity="false">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
🍀 테스트 결과의 의미
1) 테스트 코드가 실행되기 위해 스프링 프레임워크가 동작
2) 동작하는 과정에서 필요한 객체들이 스프링에 등록
3) 의존성 주입이 필요한 객체는 자동으로 주입
@Autowired
@ExtendWith
(SpringExtension.class)@ContextConfiguration
(locations = "file:src/main/webapp/WEB-INF/root-context.xml")@Log4j2
package com.multcampus.springex.sample.SampleTests;
import com.multicampus.stringex.sample.SampleService;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@Log4j2
@ExtendWith(SpringExtension.class) //spring-test를 이용하기 위한 설정 Junit5버전, Junit4는 @Runwith 사용
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class SampleTests {
@Autowired // spring에서 사용하는 의존성 주입 어노테이션
// 어노테이션을 필드, 생성자, 메서드에 적용하여 스프링 컨테이너가 해당 타입의 빈(Bean) 객체를 자동으로 주입
// 해당 타입의 빈이 존재한다면 여기에 주입해주기를 바란다.
// 스프링 컨테이너가 sampleService를 넣어줌?
private SampleService sampleService;
@Test
public void testService(){
log.info(sampleService);
Assertions.assertNotNull(sampleService);
}
// injection 되는지 확인하는 테스트 코드
@Test
public void testSampleDAO(){
log.info(sampleDAO);
Assertions.assertNotNull(sampleDAO);
}
}
package com.multicampus.springex.sample;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ToString
@Service
@RequiredArgsConstructor
public class SampleService {
private SampleDAO sampleDAO; //필드 주입
}
package com.multicampus.springex.sample;
public class SampleDAO {
}
빈(bean)
자체는 스프링 애플리케이션 컨텍스트(Application Context)의 일부동작원리
root- context.xml
: 스프링은 자신이 객체를 생성하고 관리해야 하는 객체들에 대한 설정이 필요@Component
라는 어노테이션이 존재하는 클래스의 인스턴스 생성@Autowired
로 주입 : 해당 인스턴스 변수가 스프링으로부터 자동으로 주입해달라는 표시@Controller
: MVC의 컨트롤러를 위한 어노테이션@Service
: 서비스 계층의 객체를 위한 어노테이션@Repository
: DAO 와 같은 객체를 위한 어노테이션@Component
: 일반 객체나 유틸리티 객체를 위한 어노테이션Lombok 관련 어노테이션 | Spring 관련 어노테이션 | 테스트 관련 어노테이션 |
---|---|---|
@Setter | @Autowired | @RunWith |
@Data | @Component | @ContextConfiguration |
@Log4j | @Test |
@Setter
: setter 메서드를 만들어주는 역할@Data
: @ToString, @EqualsAndHashCode, @Getter/@Setter, @RequiredArgsConstructor 결합한 형태로 한 번에 자주 사용하는 모든 메서드들 생성@Log4j
: 설정되어 있다면 로그 객체 생성@Component
: 해당 클래스가 스프링에서 객체로 만들어서 관리하는 대상임을 명시 ⇒ 빈으로 관리@Autowired
: 스프링 내부에서 자신이 특정한 객체에 의존적이므로 자신에게 해당 타입의 빈을 주입하는 의미@ContextConfiguration
: 스프링이 실행되면서 어떤 설정 정보를 읽어 들여야 하는지 명시@RunWith
: 테스트 시 필요한 클래스 지정@Test
: Junit에서 해당 메서드가 JUnit 상에서 단위 테스트의 대상인지 알려준다.인터페이스
를 이용해서 나중에 다른 클래스의 객체로 쉽게 변경할 수 있다.@Primary
@Qualifier
Q1. 스프링의 빈(Bean)으로 지정되는 객체들은 무엇일까?
A.
Q2. XML이나 애너테이션 으로 처리하는 객체 무엇일까?
package com.multicampus.springex.sample;
import org.springframework.stereotype.Repository;
public interface SampleDAO {
}
package com.multicampus.springex.sample;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
@Repository
@Primary
public class SampleDAOImpl implements SampleDAO{
}
package com.multicampus.springex.sample;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
@Repository
@Qualifier("maria")
public class SampleDAOO_Mariadb_Impl implements SampleDAO{
}
package com.multicampus.springex.sample;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@Repository
@Qualifier("oracle")
public class SampleDAOO_Oracle_Impl implements SampleDAO{
}
package com.multicampus.springex.sample;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@ToString
@Service
@RequiredArgsConstructor
public class SampleService {
private final SampleDAO sampleDAO; //객체와 객체의 의존관계의 실제 객체를 몰라도 주입이 가능하게 하는 방식 '느슨한 방식'
// 스프링 컨테이너가 주입, set, get 필요가 없어지는
/* @Autowired
*//*private SampleDAO sampleDAO; //필드 주입*/
}
lombok.copyableannotations += org.springframework.bean.factory.annotation.Qualifier
<?xml version="1.0" encoding="UTF-8"?>
<!--테스트 환경 레벨을 정함-->
<Configuration status="INFO">
<!--Appenders은 pattern으로 만들어서 console를 뿌리겠다 선언한 것-->
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<!--console에 위에 것을 등록-->
<Loggers>
<!--additivity="false"를 설정하면 해당 로거가 부모 로거의 설정을 상속받지 않는다. 즉, 독립적인 로그 레벨을 가지게 됨-->
<logger name="org.springframework" level="INFO" additivity="false">
<appender-ref ref="console"/>
</logger>
<logger name="com.multicampus" level="INFO" additivity="false">
<appender-ref ref="console"/>
</logger>
<Root level="info" additivity="false">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.multicampus.springex"/>
</beans>
dependency
에 추가implementation group: 'org.springframework', name: 'spring-webmvc', version: '5.3.19'
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<!-- 감시자를 넣음-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
전통적인 JDBC 프로그램 | MyBatis |
---|---|
직접 Connection을 맺고 마지막에 close() | 자동으로 Connection close() 기능 |
PreparedStatement 직접 생성 및 처리 | MyBatis 내부적으로 PreparedStatement 처리 |
PreparendStatement의 setXXX()등에 대한 모든 작업을 개발자가 처리 | #{prop}와 같이 속성을 지정하면 내부적으로 자동처리 |
SELETCT 경우 ResultSet이 처리 | 리턴 타입을 지정하는 경우 자동으로 객체 생성 및 ResultSet 처리 |
mybatis-spring 라이브러리를 이용
하면SqlSession
을implementation 'org.mariadb.jdbc:mariadb-java-client:3.0.4'
implementation group: 'com.zaxxer', name: 'HikariCP', version: '5.0.1'
root-context.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--데이터 소스 등록-->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="org.mariadb.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mariadb://localhost:3306/webdb"></property>
<property name="username" value="webuser"></property>
<property name="password" value="webuser"></property>
<property name="dataSourceProperties">
<props>
<prop key="cachePrepStmts">true</prop>
<prop key="prepStmtCacheSize">250</prop>
<prop key="prepStmtCacheSqlLimit">2048</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
destroy-method="**close"**>
<constructor-arg ref="hikariConfig" />
</bean>
<context:component-scan base-package="com.multicampus.springex"/>
</beans>
//mybatis와 스프링 연동
implementation group: 'org.springframework', name:'spring-jdbc', version: '5.3.19'
implementation group: 'org.springframework', name:'spring-tx', version: '5.3.19'
implementation 'org.mybatis:mybatis:3.5.9'
implementation 'org.mybatis:mybatis-spring:2.0.7'
// modelmapper
implementation group: 'org.modelmapper', name: 'modelmapper', version: '3.0.0'
implementation group: 'org.hibernate', name: 'hibernate-validator', version: '6.2.1.Final'
SqlSessionFactory
설정SqlSessionFactory
에서 생성하는 SqlSession을 통해서 수정SqlSessionFactoryBean
: 스프링에 SqlSessionFactory 등록하는 작업을 해줌<!--세션에다가 connection을 연결해주겠다 = sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name=**"dataSource"** ref="dataSource"/>
</bean>
package com.multicampus.springex.mapper;
import org.apache.ibatis.annotations.Select;
public interface TimeMapper { //데이터베이스의 현재시각을 문자열로 받아와서 처리
@Select("select now()") //반복되는 것을 객체처럼 만들어서 재사용, 문자열로 넘어가기 때문에 ; 지정하지 않음
String getTime();
}
root-context.xml
에 인터페이스 연동<!--timeMapper와 연결해줘, 빈에 등록할거야의 의미-->
<context:component-scan base-package="com.multicampus.springex"/>
<mybatis:scan base-package="com.multicampus.springex.mapper" ></mybatis:scan>
package com.multicampus.springex.mapper;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class TimeMapperTests {
@Autowired(required = false)
private TimeMapper timeMapper;
@Test
public void testGetTime(){
log.info(timeMapper.getTime());
}
}
💡 항상 웬만하면 오타문제다 ㅠㅠㅠㅠ 주의하자. 에러를 잘 못봐도 한 번 더 찬찬히 코드를 살펴보자
오늘은 그래도 나름 열심히 복습한 느낌? 대신 이것을 잊지 않기 위해 며칠 뒤에 다시 복습하자.
오늘 트위터에서 한 기획자이자 개발자이신 분의 트위터 내용을 보고 진짜... 공감을 많이 받았다. 불안의 연속과 그럼에도 열심히해서 취업하시고 계속 성장해나가는 얘기..그리고 저장해두고 안본 강의들 많다는 것 또한 공감했으며 기획자들 만나는 자리에서 다들 대단하신 분들이 많아서 뒤쳐졌다는 생각을 하며, 엘리트 코스가 부러웠다고 한다.
나도 그런 것 같다. 1년 준비해서 비전공으로 네카라쿠배가는 사람들을 보면서 흔들리는 것 나는 몇년을 공부했는데 그 사람들보다 못하며 지금 부트캠프에서도 나만 뒤쳐지는 것 같은 것 이 불안감은 어쩔 수가 없는 것 같다. 그래도 대학생활 내내 쉬지 않고 아르바이트, 근로 하면서 공부해온 것 그것도 대단한 것 잖아.. 주변에 친한 애들도 열심히 산다고 생각하며 얘기해주는데 나는 아무것도 안한 것 같아라는 생각만 반복이다. 하지만 아예 무의미하지는 않겠지.. 그래도 열심히 살아왔잖아. 그 자리에서 남과 비교하면서 좌절하지 않으면 열심히 하면 나도 해낼 수 있지 않을까? 화이팅하자.