반응형

테스트 자동화에 대해 알아보겠습니다.

 

개발을 진행하다보면 다양한 함수를 작성하고 적용하는 과정에서 다양한 케이스별 테스트를 진행하게 됩니다.

단순한 코드라면 테스트도 크게 어렵지 않고 실수나 잘못된 연산 있더라도 쉽게 수정이 가능하지만, 복잡하고 케이스별로 오류가 발생할수 있는 함수를 작성 중이라면, 아래와 같은 경우를 많이 만나 볼 수 있습니다.

 

1. test( params ); 라는 함수를 작성하였다.

2. test(1)은 정상 동작한다.

3. test(2)는 오류가 발생한다.

4. test(2)에 대한 오류를 수정하고, test(2)가 정상 동작한다.

> 수정을 했으니 이제 정상 동작하는 소스일까? ===> 아니오

수정으로 인해 test(2)는 정상이 되었지만, 정상동작하던 test(1)이 오류가 발생할 수도 있고 다른 케이스별 오류가 발생할 수 있다.

 

개발을 하다보면 생각보다 굉장히 자주 만나는 현상입니다.

그럼 개발자는 무엇을 어떻게 할까요?

정상 동작을 하는지 유형 별로 정리를 하고 작성한 코드를 테스트 > 수정 > 테스트를 반복합니다.

 

이렇게 수동으로 테스트를 진행하다보면 에러가 발생할 여지가 있습니다.

 

 

 

 

 

 

BDD방법론

BDD는 테스트(test), 문서(documentation), 예시(example)를 한데 모아놓은 개념으로 실제 개발사례를 통해 BDD에 대해 알아보겠습니다.

 

 

 

 

거듭제곱 함수를 통해 자동화 테스트 적용해보기

pow라는 이름의 함수를 만들고 BDD에 적용해보겠습니다.
(자바스크립트 자체에 ** 연산자나 Math.pow 메소드를 활용해도 되지만 BDD 학습을 위해 작성합니다.)
 
describe("거듭제곱 출력 함수", function() {
	it("매개변수의 n제곱", function() {
		assert.equal(pow(2, 3), 8);
	});
});
 

describe("title", function(){...})

구현하고자 하는 기능에 대해 설명이 들어갑니다. 여기선 pow함수에 대한 설명을 입력합니다.

 

it("유형별 설명", function(){...})

it의 첫번째 인수엔 특정 유스 케이스에 대한 설명이 들어갑니다. 이곳에는 누가 읽고 이해할 수 있도록 자연어로 작성합니다. 두번째 인수엔 테스트 할 함수가 들어갑니다.

 

assert.equal(value1, value2)

기능을 제대로 작성했다면 해당 구문이 오류없이 실행됩니다.

value1과 value2가 일치하지 않는다면 오류가 발생합니다.

value1에 pow(3, 3)를 value2에는 81을 입력하면 일치할 것이고 value2에 다른값을 입력하면 에러가 발생합니다.

 

 

사용되는 라이브러리

3가지의 라이브러리를 활용합니다.

Mocha - describe, it와 같은 테스팅함수와 테스트 관련 주요함수를 제공합니다.

Chai - 다양한 assertion을 제공하는 라이브러리입니다. (assert.equal)

Sison - 함수의 정보를 캐내는 라이브러리로 내장 함수를 모방합니다.

 

 

 

 

 

자동화 테스트해보기

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css"> <!-- Mocha css -->
</head>
<body>
    <div id="mocha"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script> <!-- Mocha -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script> <!-- chai -->
    <script>
        let assert = chai.assert;
        window.onload = function(){
            mocha.setup('bdd'); // mocha setup

            describe("pow함수 검증하기", function(){
                it("주어진 숫자의 n 제곱", function(){
                    assert.equal(pow(3, 3), 27);
                    assert.equal(pow(3, 4), 81);
                    assert.equal(pow(3, 5), 243);
                });

            });

            mocha.run();
        };
        
        //pow 작성
        function pow(x, n) {
            if(n < 2){
                return x;
            }
            return x * pow(x, --n);
        }
    </script>
</body>
</html>

거듭제곱 메소드를 재귀함수를 통해 작성하였습니다.

작성한 pow를 테스트화 시킨 코드입니다. 아래에서 테스트 결과를 보겠습니다.

 

결과

테스트 정상 화면

title과 테스트 결과가 정상적인경우 위 사진처럼 발생합니다.

 

 

 

 

 

오류 발생시키기

describe("pow함수 검증하기", function(){
    it("주어진 숫자의 n 제곱", function(){
        assert.equal(pow(3, 3), 27);
        assert.equal(pow(3, 4), 81);
        assert.equal(pow(3, 5), 243);
        assert.equal(pow(2, 2), 222);
    });
});

일부러 마지막에 오류를 내면 어떻게 될까요?

 

오류 결과

정상동작하는 3개에 대해선 문제가 없지만 오류가 발생중인 부분에 대해서 오류 메시지를 출력해줍니다.

오류메시지를 세분화 해보겟습니다.

 

 

 

 

 

오류메시지 분기하기

describe("pow함수 검증하기", function(){
    it("주어진 숫자의 n 제곱 3^3 = 27", function(){
        assert.equal(pow(3, 3), 27);
    });
    it("주어진 숫자의 n 제곱 3^4 = 81", function(){
        assert.equal(pow(3, 4), 81);
    });
    it("주어진 숫자의 n 제곱 3^5 = 243", function(){
        assert.equal(pow(3, 5), 243);
    });
    it("주어진 숫자의 n 제곱 2^2 = 4", function(){
        assert.equal(pow(2, 2), 222);
    });
});

각 유형별로 it로 나누고 각각의 설명을 기재하였습니다.

아래는 오류 메시지 결과입니다.

 

분기처리한 오류 메시지 결과

체크박스 형태로 성공은 체크, 실패한 부분은 x와 함께 메시지를 출력해주고 있네요.

유형별 결과를 한눈에 보기엔 분기처리 한 것이 보기 좋은것 같습니다.

 

 

 

 

 

자료 출처: https://ko.javascript.info/testing-mocha

 

테스트 자동화와 Mocha

 

ko.javascript.info

반응형
반응형

신규 고도화 사업을 진행하게되어 기존 소스를 받았는데, 구조가 뭔가 이상하다.

 

분명히 전자정부프레임워크라고 했는데, egov관련된 정보는 어디에도 안보이고 pom.xml부터 에러가 발생한다.

pom.xml에 parent라는 태그가 보인다.

찾아보니 메이븐을 부모, 자식형태로 구성해서 공통화를 할 수 있다고 한다.

오... 같은 프로젝트에 공통으로 쓰는 라이브러리가 많을때 쓰면 좋을 것 같다. (근데 전자정부인데 이렇게 마음대로 구조를 나눠도 되나...?😨)

 

pom.xml parent, modules구조로 나누기

먼저 조상(부모)가 될 프로젝트를 구성한다. (egov기준으로 예시를 작성하겠습니다.)

해당 프로젝트에선 딱히 뭔가를 만들지 않고, pom.xml만 작성한다.

 

Parent Project

1. 신규 프로젝트 생성

 

2. Project name, Group id를 작성한다.

egov-prj-parent 프로젝트 생성

 

3. egov구조 sample데이터도 전부 체크해준다.

 

4. 프로젝트가 생성되면 프로젝트 우클릭 > Properties > Java Build Path로 갑니다.

각 항목들을 모두 제거해서 프로젝트를 비워줍니다.

 

제거하면 다 사라지고 껍데기만 남는데 필요없는 src폴더까지 지워줍니다.

 

5. pom.xml을 수정합니다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.psw</groupId>
	<artifactId>egov-prj-parent</artifactId>
	<version>1.0.0</version>
	<packaging>pom</packaging>
	<name>egov-prj-parent</name>
	<url>http://www.egovframe.go.kr</url>

	<licenses>
        <license>
            <name>The Apache Software License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
        </license>
    </licenses>

	<properties>
	    <spring.maven.artifact.version>4.3.25.RELEASE</spring.maven.artifact.version>
		<egovframework.rte.version>3.10.0</egovframework.rte.version>
	</properties>
	
	<modules>
        <module>egov-prj-child1</module>
        <module>egov-prj-child2</module>
    </modules>

	<repositories>
        <repository>
            <id>mvn2s</id>
            <url>https://repo1.maven.org/maven2/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
			<id>egovframe</id>
			<url>http://maven.egovframe.go.kr/maven</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
        <repository>
            <id>egovframe_old1</id>
            <url>http://maven.egovframe.kr:8080/maven/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>egovframe_old2</id>
            <url>http://www.egovframe.go.kr/maven/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

	<dependencies>
		<!-- 표준프레임워크 실행환경 -->
        <dependency>
		    <groupId>egovframework.rte</groupId>
		    <artifactId>egovframework.rte.ptl.mvc</artifactId>
		    <version>${egovframework.rte.version}</version>
		    <exclusions>
		    	<exclusion>
		    		<artifactId>commons-logging</artifactId>
		    		<groupId>commons-logging</groupId>
		    	</exclusion>
		    </exclusions>
        </dependency>
        <dependency>
		    <groupId>egovframework.rte</groupId>
		    <artifactId>egovframework.rte.psl.dataaccess</artifactId>
		    <version>${egovframework.rte.version}</version>
        </dependency>
        <dependency>
			<groupId>egovframework.rte</groupId>
			<artifactId>egovframework.rte.fdl.idgnr</artifactId>
			<version>${egovframework.rte.version}</version>
		</dependency>
       	<dependency>
			<groupId>egovframework.rte</groupId>
			<artifactId>egovframework.rte.fdl.property</artifactId>
			<version>${egovframework.rte.version}</version>
		</dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
		    <groupId>javax.servlet</groupId>
		    <artifactId>jstl</artifactId>
		    <version>1.2</version>
        </dependency>

        <dependency>
		    <groupId>taglibs</groupId>
		    <artifactId>standard</artifactId>
		    <version>1.1.2</version>
        </dependency>

		<dependency>
	        <groupId>org.antlr</groupId>
	        <artifactId>antlr</artifactId>
	        <version>3.5</version>
   		</dependency>

		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.5.0</version>
		</dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-jsp</artifactId>
            <version>3.0.8</version>
        </dependency>
	</dependencies>

	<build>
        <defaultGoal>install</defaultGoal>
        <directory>${basedir}/target</directory>
        <finalName>${artifactId}-${version}</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
	                <groupId>org.apache.tomcat.maven</groupId>
	                <artifactId>tomcat7-maven-plugin</artifactId>
	                <version>2.2</version>
	                <configuration>
	                    <port>80</port>
	                    <path>/</path>
	                    <systemProperties>
	                        <JAVA_OPTS>-Xms256m -Xmx768m -XX:MaxPermSize=256m</JAVA_OPTS>
	                    </systemProperties>
	                </configuration>
	            </plugin>
                <plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.1</version>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
						<encoding>UTF-8</encoding>
						<maxmem>1024m</maxmem>
					</configuration>
				</plugin>
				<plugin>
                	<groupId>org.apache.maven.plugins</groupId>
                	<artifactId>maven-war-plugin</artifactId>
                	<version>2.4</version>
                	<configuration>
                		<failOnMissingWebXml>false</failOnMissingWebXml>
                	</configuration>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>hibernate3-maven-plugin</artifactId>
                    <version>3.0</version>
                    <configuration>
                        <components>
                            <component>
                                <name>hbm2ddl</name>
                                <implementation>annotationconfiguration</implementation>
                            </component>
                        </components>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>org.hsqldb</groupId>
                            <artifactId>hsqldb</artifactId>
                            <version>2.5.0</version>
                        </dependency>
                    </dependencies>
                </plugin>
                <!-- EMMA -->
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>emma-maven-plugin</artifactId>
                    <version>1.0-alpha-3</version>
                </plugin>
                <!-- PMD manven plugin -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-pmd-plugin</artifactId>
                    <version>3.12.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <!-- EMMA -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skipTests>true</skipTests>
                    <forkMode>once</forkMode>
                    <reportFormat>xml</reportFormat>
                    <excludes>
                        <exclude>**/Abstract*.java</exclude>
                        <exclude>**/*Suite.java</exclude>
                    </excludes>
                    <includes>
                        <include>**/*Test.java</include>
                    </includes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>emma-maven-plugin</artifactId>
                <inherited>true</inherited>
            </plugin>
            <!-- JavaDoc -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>3.1.1</version>
            </plugin>
        </plugins>
    </build>
    <reporting>
        <outputDirectory>${basedir}/target/site</outputDirectory>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
                <reportSets>
                    <reportSet>
                        <id>sunlink</id>
                        <reports>
                            <report>javadoc</report>
                        </reports>
                        <inherited>true</inherited>
                        <configuration>
                            <links>
                                <link>http://docs.oracle.com/javase/6/docs/api/</link>
                            </links>
                        </configuration>
                    </reportSet>
                </reportSets>
            </plugin>
            <!-- JUnit Test Results & EMMA Coverage Reporting -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>emma-maven-plugin</artifactId>
                <inherited>true</inherited>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>surefire-report-maven-plugin</artifactId>
                <inherited>true</inherited>
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>report-only</report>
                        </reports>
                    </reportSet>
                </reportSets>
            </plugin>
            <!-- Generating JavaDoc Report -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <configuration>
                    <minmemory>128m</minmemory>
                    <maxmemory>512m</maxmemory>
                    <encoding>${encoding}</encoding>
                    <docencoding>${encoding}</docencoding>
                    <charset>${encoding}</charset>
                </configuration>
            </plugin>
            <!-- Generating Java Source in HTML -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jxr-plugin</artifactId>
                <configuration>
                    <inputEncoding>${encoding}</inputEncoding>
                    <outputEncoding>${encoding}</outputEncoding>
                    <linkJavadoc>true</linkJavadoc>
                    <javadocDir>apidocs</javadocDir>
                </configuration>
            </plugin>
        </plugins>
    </reporting>
</project>

너무나도 기네요...

공통으로 사용할 메이븐은 parent에 넣고 사용할 예정이고 여기서 테스트를 위해 tiles만 추가해두겠습니다.

 

modules부분에서 하위 프로젝트를 지정합니다.

 

Child1 Project

1. child1 프로젝트 생성하기

 

2. egov 프로젝트를 생성하고 pom.xml을 바로 수정합니다.

(설정이나 삭제 과정은 없습니다.)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.psw</groupId>
		<artifactId>egov-prj-parent</artifactId>
		<version>1.0.0</version>
	</parent>
	<artifactId>egov-prj-child1</artifactId>
	<packaging>war</packaging>
	<version>1.0.0</version>
	<name>egov-prj-child1</name>
	<url>http://www.egovframe.go.kr</url>

	<licenses>
		<license>
			<name>The Apache Software License, Version 2.0</name>
			<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
		</license>
	</licenses>

	<properties>
		<spring.maven.artifact.version>4.3.25.RELEASE</spring.maven.artifact.version>
		<egovframework.rte.version>3.10.0</egovframework.rte.version>
	</properties>

	<repositories>
		<repository>
			<id>mvn2s</id>
			<url>https://repo1.maven.org/maven2/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>egovframe</id>
			<url>http://maven.egovframe.go.kr/maven</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>egovframe_old1</id>
			<url>http://maven.egovframe.kr:8080/maven/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>egovframe_old2</id>
			<url>http://www.egovframe.go.kr/maven/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<dependencies>

	</dependencies>

	<build>
		<defaultGoal>install</defaultGoal>
		<directory>${basedir}/target</directory>
		<finalName>${artifactId}-${version}</finalName>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.tomcat.maven</groupId>
					<artifactId>tomcat7-maven-plugin</artifactId>
					<version>2.2</version>
					<configuration>
						<port>80</port>
						<path>/</path>
						<systemProperties>
							<JAVA_OPTS>-Xms256m -Xmx768m -XX:MaxPermSize=256m</JAVA_OPTS>
						</systemProperties>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.1</version>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
						<encoding>UTF-8</encoding>
						<maxmem>1024m</maxmem>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-war-plugin</artifactId>
					<version>2.4</version>
					<configuration>
						<failOnMissingWebXml>false</failOnMissingWebXml>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>hibernate3-maven-plugin</artifactId>
					<version>3.0</version>
					<configuration>
						<components>
							<component>
								<name>hbm2ddl</name>
								<implementation>annotationconfiguration</implementation>
							</component>
						</components>
					</configuration>
					<dependencies>
						<dependency>
							<groupId>org.hsqldb</groupId>
							<artifactId>hsqldb</artifactId>
							<version>2.5.0</version>
						</dependency>
					</dependencies>
				</plugin>
				<!-- EMMA -->
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>emma-maven-plugin</artifactId>
					<version>1.0-alpha-3</version>
				</plugin>
				<!-- PMD manven plugin -->
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-pmd-plugin</artifactId>
					<version>3.12.0</version>
				</plugin>
			</plugins>
		</pluginManagement>
		<plugins>
			<!-- EMMA -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<skipTests>true</skipTests>
					<forkMode>once</forkMode>
					<reportFormat>xml</reportFormat>
					<excludes>
						<exclude>**/Abstract*.java</exclude>
						<exclude>**/*Suite.java</exclude>
					</excludes>
					<includes>
						<include>**/*Test.java</include>
					</includes>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>emma-maven-plugin</artifactId>
				<inherited>true</inherited>
			</plugin>
			<!-- JavaDoc -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-javadoc-plugin</artifactId>
				<version>3.1.1</version>
			</plugin>
		</plugins>
	</build>
	<reporting>
		<outputDirectory>${basedir}/target/site</outputDirectory>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-project-info-reports-plugin</artifactId>
				<version>3.0.0</version>
				<reportSets>
					<reportSet>
						<id>sunlink</id>
						<reports>
							<report>javadoc</report>
						</reports>
						<inherited>true</inherited>
						<configuration>
							<links>
								<link>http://docs.oracle.com/javase/6/docs/api/</link>
							</links>
						</configuration>
					</reportSet>
				</reportSets>
			</plugin>
			<!-- JUnit Test Results & EMMA Coverage Reporting -->
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>emma-maven-plugin</artifactId>
				<inherited>true</inherited>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>surefire-report-maven-plugin</artifactId>
				<inherited>true</inherited>
				<reportSets>
					<reportSet>
						<reports>
							<report>report-only</report>
						</reports>
					</reportSet>
				</reportSets>
			</plugin>
			<!-- Generating JavaDoc Report -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-javadoc-plugin</artifactId>
				<configuration>
					<minmemory>128m</minmemory>
					<maxmemory>512m</maxmemory>
					<encoding>${encoding}</encoding>
					<docencoding>${encoding}</docencoding>
					<charset>${encoding}</charset>
				</configuration>
			</plugin>
			<!-- Generating Java Source in HTML -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jxr-plugin</artifactId>
				<configuration>
					<inputEncoding>${encoding}</inputEncoding>
					<outputEncoding>${encoding}</outputEncoding>
					<linkJavadoc>true</linkJavadoc>
					<javadocDir>apidocs</javadocDir>
				</configuration>
			</plugin>
		</plugins>
	</reporting>
</project>

parent부분에 부모로 받을  groupId와 artifactId를 매칭해줍니다.

<dependencies>부분은 비웁니다. 부모에서 상속받은 메이븐만 사용할 예정입니다.

 

3. 해당 워크스페이스가 동작하고 있는 경로로가서 child1프로젝트를 parent프로젝트로 넣습니다.

parent프로젝트 복사한다.

나중에 child2도 생성해서 똑같이 복사하면 됩니다. 지금은 1만 복사합니다.

 

4. eclipse로 돌아와서 프로젝트를 새로고침 해보면, child1이 들어와있는걸 볼 수 있습니다.

parent 안에 child1

 

5. 필요없어진 egov-prj-child1은 지웁니다.

 

6. 이제 개발해야할 child1의 소스를 import하겠습니다.

parent안에 있는 child1를 해야합니다.

 

Package Explorer - 우클릭 - import
Maven - Exsiting Maven Projects
child1 선택

 

그럼 정상적으로 import가 된다.

지웠던 egov-prj-child1가 생성되었다.

 

7. child1프로젝트에 타일즈 세팅후 톰캣 구동을 해보니 정상적으로 페이지가 뜬다. 😆

tiles 적용 완료

 

Child2 Project

child1 프로젝트와 마찬가지로 동일하게 설정하고 import후 개발을 진행하면 된다.

반응형
반응형

웹 개발을 하면서 HTML 태그 요소에 가장 많이 사용하는건, ID와 class 속성일 겁니다.

제가 알고있는 지식으로는 id는 숫자로 시작하면 안되고, 문자열로 시작해야한다로 알고 있었는데, 일반 정수형 데이터를 넣어도 잘 적용이 되고, js를 통해 제어가 가능했습니다.

 

그 예제는 아래와 같습니다.

 

HTML id속성이 정수형이면 어떻게 될까?

id값에 정수형으로 태그 생성하기

const h1 = document.createElement("h1");
h1.id = 11223344;
h1.innerText = "TEST";
document.body.append(h1);

정수형으로 id값을 생성하고 js를 통해 h1태그의 텍스트 값을 변경해보겠습니다.

생성된 태그를 확인해보면 정수형으로 생성했지만 문자열형태로 id값까지 h1태그가 잘 생성된 걸 볼 수 있습니다.

 

document.getElementById("11223344").innerText = "변경";

숫자로 시작하는 id값을 호출하여 text값을 변경해도 문제없이 동작합니다.

 

숫자 ID값에 css적용하기?

<style>
  #11223344{color: yellowgreen}
</style>

다만 style태그에 정의한 css는 동작하지 않습니다.

 

 

 

HTML - id란? 특징

HTML - ID속성

ID는 document 전체에서 유일한 고유식별자로 유일해야합니다.

요소를 구분하고 스타일(css), 스크립트(js)에서 특정 요소를 식별하고 제어하기 위해 주로 사용됩니다.

 

ID의 특징

- 요소마다 한개의 ID만 가질 수 있습니다. 

- 공백(스페이스, 탭)의 값을 포함하면 안됩니다. 혹시 공백을 넣게되면 공백까지 ID로 취급됩니다.

- 대소문자를 구분합니다.

 

 

 

HTML버전별 ID 동작 차이

HTML4

id는 A-Z, a-z로 시작해야하며, 이후 문자, 숫자, '-', '_', '.'이 올 수 있습니다.

 

HTML5

HTML5로 올라오면서 공백을 제외하고 대부분의 값을 받아들입니다.

숫자로만 구성되거나, 밑줄로 시작하거나 점만으로 구성되어도 가능합니다.

 

4, 5는 이러한 차이점이 발생하고 있는데, 5로 올라오면서 허용된 대부분의 값이 문제가 되고 있습니다.

특히나 css에서 .과 :는 선택자로서 특별한 의미를 지니는데, id값에 .과 :가 있게되면 css측에서는 혼돈이 발생됩니다.

 

 

"."이 있는 id값에 css적용하기

<style>
  #abc.def{color: yellowgreen} //abc라는 id를 가지면서 def라는 클래스를 가진 요소에 적용
  #abc\.def{color: yellowgreen} //\역슬래쉬를 통해 처리
</style>

<h1 id="abc.def">TEST</h1>

안그래도 복잡한 구조에 역슬래쉬까지 추가해서 제어를 해야합니다.

저라면 정말 어쩔수 없는 특별한 상황이 아니라면 사용하고 싶지 않군요.

 

다른 스택오버플로우나 블로그 글들에서도 볼 수 있듯 숫자로 시작하는 id값 생성과 제어는 js까진 사용 가능하지만

html4처럼 기존의 양식을 지키는 형태로 작성하며, 문자[A-Za-z]로 시작하고, ':', '.'와 같은 특수문자보단 '-', '_'를 사용하거나 카멜표기법을 따르는것을 권장합니다.

 

 

 

 

자료출처: https://stackoverflow.com/questions/70579/what-are-valid-values-for-the-id-attribute-in-html

자료출처: https://www.w3.org/TR/CSS21/syndata.html#characters

 

반응형
반응형

자료 출처 :

- https://medium.com/@gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec

- https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf

- https://new93helloworld.tistory.com/358

- https://velog.io/@jakeseo_me/2019-03-15-2303-%EC%9E%91%EC%84%B1%EB%90%A8-rmjta5a3xh

- https://meetup.toast.com/posts/89

 

웹 개발자로 년차가 쌓이고 있는 현 시점에서 자바스크립트의 내부 동작을 이해할 필요가 있었고, 정보들을 찾아보면서 원문과 해당 글들을 해석, 분석하신 분들의 글 등을 읽어보고 개인적으로 정리해보기 위해 작성하는 글입니다.

원문이나 기존 글들이 너무 좋아서 해당 글들을 먼저 보시는게 좋을 것 같습니다 :)

 


javascript 콜스택(call stack), 이벤트루프(event Loop)의 개념

원문 글들에서도 보이듯 현대의 자바스크립트 영향력은 기존 웹 페이지의 동적인 변화만을 위한 언어가 아닌 프론트, 백엔드, DB(CouchDB), 임베디드 프로그래밍 등 다양한 영역에서 영향력을 보이고 있습니다.

이렇게 다양한 기능을 하는 자바스크립트는 어떻게 동작이 되고 있는지 궁금해졌고 관련 키워드들을 보면서 콜스택, 이벤트 루프와 같은 개념들이 나왔습니다.🤔

이러한 키워드들의 내용을 학습하고 이해하는 것이 이번 글의 목표입니다.

 

 

자바스크립트 엔진

“V8” 웹 개발을 하면서 한번 정도는 들어봤을 자바스크립트 엔진입니다. 크롬 브라우저, Node.js에서 동작하는데, 메모리힙과 콜스택으로 이루어져 있습니다. 엔진이 코드를 읽고 실행하면서 변수나 함수 객체등을 담아놓는 공간(메모리 힙)과 실행할 코드를 저장하는 공간(콜 스택)으로 나뉩니다.

-메모리 힙(Memory Heap): 변수, 함수, 객체등이 선언되면 자동으로 할당하고 불필요하면 자동으로 해제되는데(가비지 컬렉터) 이곳에 저장이 된다.

-콜 스택(Call Stack): 실행해야 할 코드를 쌓는 공간이다.

 

 

Web API

브라우저에는 엔진뿐만 아니라 Web API가 존재하는데 자바스크립트 개발자들이 사용하는 다양한 API(DOM,  setITimeout, Ajax)들은 자바스크립트 엔진이 아닌 브라우저의 Web API를 통해 제공됩니다.

이러한 API를 통제하기 위해서 Event Loop, Task Queue가 존재합니다.

 

 

콜 스택(Call Stack)

먼저 자료구조 스택(stack)에 대해서 간단히 알아보겠습니다.

쌓는다는 뜻으로 가장 마지막에 들어온게 먼저 나가는 구조로 후입선출 구조입니다. 삽입은 push, 삭제는 pop입니다.

 

자바스크립트는 싱글 스레드 언어입니다.

즉, 한 번에 한 개의 일만 처리 할 수 있다는 뜻이죠.

콜 스택이란 함수의 호출 순서를 기록하는 자료구조로 어떤 함수를 실행하면 콜 스택에 추가(push)되며 함수로부턴 반환받을 때 콜 스택의 맨 위를 가져(pop)옵니다.

 

-콜 스택의 동작 예시

function tenSum(x){
	return x + 10;
}

function printSum(x){
	const sum = tenSum(x);
	console.log(sum);
}
printSum(5);

call stack 동작

함수 선언은 메모리 힙에 올라가고 콜 스택은 실행될 함수들이 쌓인다고 했었죠?

Stack1 printSum(5)를 실행하기 위해 push됩니다.

Stack2 printSum(5)안에 tenSum(x)를 실행하고 있습니다 push됩니다.

Stack3 tenSum(x)가 15를 리턴하면서 tenSum(x)는 pop되고, console.log(sum)을 push합니다.

Stack4 console.log(sum)가 리턴(출력)하면서 pop됩니다.

Stack5 printSum(5)가 리턴하면서 pop됩니다.

 

 

싱글 스레드 방식의 문제점

일반적으로 작성한 코드의 경우 문제점이 전혀 없지만 단 하나의 실행함수가 처리해야할게 너무 많아서 다음 함수로 못넘어가고 콜 스택에서 차지하고 있으면 어떻게 될까요?🙄

다음 함수들은 앞의 함수가 끝나길 무기한 기다릴 것이고, 최악의 상황엔 브라우저가 응답이 없다는 메시지와 함께 페이지가 뻗어버리는 현상💢이 발생할 수도 있습니다. (신입시절 실시간(약 3초마다)으로 수천개의 데이터를 불러와서 갱신하는 페이지가 있었는데 반복문 하나를 잘 못 짜서 페이지가 뻗는현상을 경험해봤습니다... 😥)

 

하지만 자바스크립트로 만드는 페이지에서 싱글 스레드라고 느껴본적이 있었나요?

페이지에서 애니메이션이 돌면서 클릭이벤트와 키이벤트가 동시에 이루어지고 한번에 여러곳에 비동기 데이터를 요청을 하면서 프로그레스바같은 애니메이션이 돌고 있습니다.

이런 행위가 가능하게 하는 것은 '이벤트 루프(Event Loop)'덕분입니다.

 

 

이벤트 루프?

자바스크립트 엔진에 존재하는 콜 스택은 요청이 들어올때 해당 요청을 순차적으로 담아서 처리를 한다. 그럼 비동기 요청과 동시성 처리(애니메이션이 돌면서 클릭이벤트 등)는 어떤식으로 처리가 될까?

 

브라우저의 환경은 다음과 같다.

출처:&nbsp;https://meetup.toast.com/posts/89

그림을 살펴보면 우리가 자주 사용하던 API들인 setInterval, XMLHttpRequest와 같은 함수들은 웹 API로 따로 존재하며, 해당 함수들을 자바스크립트 엔진이 제공하지 않는다. 그 외에도 이벤트 루프테스크 큐를 볼 수 있다.

 

자바스크립트는 싱글 스레드 언어라는 말은 엔진이 단일 호출 스택을 사용되는 관점에서 파생된 말이고, 실제 브라우저 환경에서는 여러 개의 스레드가 동작하며 연동을 위해 사용되는 장치가 '이벤트 루프(Event Loop)'이다.

 

 

테스크 큐(Task Queue), 이벤트 루프(Event Loop)

그럼 비동기 Web API 요청들은 어떻게 동작할까?

 

예시 코드를 보겠다.

function run(){
	console.log("run");
	foo();
}

function foo(){
	console.log("foo");
}

function bar(){
	console.log("bar");
}

setTimeout(bar, 10);
run();

동작 결과

 

bar를 먼저 실행하지만 setTimeout 10ms로 인해 run, foo, bar 순으로 console.log가 찍힌다.

실제로 run, foo가 오래걸리는 상황이라 10ms보다 오래걸려도 위 순서는 지켜진다.

 

어떻게 setTimeout함수는 foo가 끝나자마자 bar를 실행 시켰을까?

바로 테스크 큐와 이벤트 루프가 처리 했기 때문이다.

이벤트 루프는 호출 스택이 비워질 때마다 큐에서 콜백함수를 꺼내와서 실행하는 역할을 해준다.

예제를 기준으로 처음 실행될 때, 해당 코드는 "현재 실행중인 테크스"가 된다.

코드를 실행하는 도중 10ms가 지나면 bar를 바로 실행하지 않고 테스크 큐에 추가한다.

이벤트 루프는 "현재 실행중인 테스크"가 종료되면 테스크 큐에 대기중인 첫번째 테스크를 가져와서 실행한다.

 

 

 

 

이벤트 루프는 주기적으로 "현재 실행중인 테스크"가 있는지 체크하고 없다면 "테스크 큐에 테스크가 있는지"를 반복적으로 확인한다.

 

즉, 모든 비동기 API들은 작업이 완료되면 콜백 함수를 테스크 큐에 추가해두고 자신의 순서를 기다린다.

"현재 실행중인 테스크"가  없으면 테스크큐의 첫번째 큐("콜백함수")를 가져와서 실행한다.

반응형
  1. 익명 2022.05.13 22:32

    비밀댓글입니다

    • Favicon of https://myhappyman.tistory.com Park.S.W 2022.05.16 09:01 신고

      앗.. 너무 급하게 조사해서 정리한거같네요 ㅋㅋㅋㅋ 다시 한번 확인하고 수정해서 공개하겠습니다~

반응형

데이터 시각화 작업을 하다보면 다양한 오픈소스와 차트 라이브러리를 사용하게 되는데, 그 중 오픈소스이며 MIT 라이선스를 따르는 chart.js로 세계지도 만들기 예제를 알아보겠습니다.🚀

공식 홈페이지를 가도 샘플페이지등을 확인해봐도 지도형 차트는 보이지 않는데, github 지도 관련하여 배포중인 사이트가 있습니다.

 

 

https://github.com/sgratzl/chartjs-chart-geo

 

GitHub - sgratzl/chartjs-chart-geo: Chart.js Choropleth and Bubble Maps

Chart.js Choropleth and Bubble Maps. Contribute to sgratzl/chartjs-chart-geo development by creating an account on GitHub.

github.com

위 페이지를 들어가보면 다양한 모양의 지도차트를 볼 수 있습니다. 제공중인 샘플소스를 CodePen페이지를 통해 제공중이니 적용해보셔도 좋습니다.

 

 

Chart.js 세계지도 데이터 시각화하기

필요한 리소스

- chart.js 지도 geo처리 > https://unpkg.com/chartjs-chart-geo@3.5.2/build/index.umd.min.js

- 지도를 그리기 위한 위 경도  json정보 > https://unpkg.com/world-atlas@2.0.2/countries-50m.json

 
world-atlas는 세계 지리 위 경도 지도정보를 제공하고 있습니다.
해당 url로 fetch함수를 통해 json데이터 받고, chart.js geo에 맞도록 가공하여 세팅합니다.
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chart.js 세계지도</title>
</head>
<body>
    <div>
        <canvas id="myChart"></canvas>
    </div>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script type="text/javascript" src="https://unpkg.com/chartjs-chart-geo@3.5.2/build/index.umd.min.js"></script>
<script>
const url = "https://unpkg.com/world-atlas@2.0.2/countries-50m.json"; //세계지도를 그리기 위한 json
fetch(url).then(result => result.json()).then((datapoint) => {
    //나라 뽑아내기
    const countries = ChartGeo.topojson
                        .feature(datapoint, datapoint.objects.countries).features;

    //차트를 그리기 위한 데이터셋
    const data = {
        labels: countries.map(country => country.properties.name),
        datasets: [{
            label: 'Countries',
            data: countries.map(country => ({feature: country, value: Math.random()*100})) //노출데이터는 랜덤값으로 표기
        }]
    };
    
    //차트 옵션
    const config = {
        type: 'choropleth', //차트 모양 > 지구본 타입 설정
        data,
        options: {
            showOutline: true, //지구본 원형라인 잡아주기
            showGraticule: true, //지구본 위, 경도선 그려주기
            scales: {
                xy: {
                    projection: 'equalEarth' //지구본 모양으로 변경
                }
            },
            plugins: {
                legend: {
                    display: false //chart.js차트 범례 숨기기(별도 표기가 됨)
                }
            }
        }

    };

    const myChart = new Chart(
        document.getElementById('myChart'),
        config
    );  
});
</script>
</body>
</html>

상세 옵션정보는 주석 또는 chart.js geo github페이지를 확인해주세요.

 

결과물

chart.js를 활용한 세계지도 차트

값은 랜덤으로 설정하여 페이지 로드시 매번 바뀌는걸 볼 수 있습니다.

반응형
반응형

프로젝트 기능을 수행하다보면 각종 다양한 접근 방식이나 데이터 처리로 인해 오류를 만나게 됩니다.

이런 경우 하나의 서비스를 처리하면서 데이터에 대한 일관적인 상태를 유지하고 문제가 없게 관리를 해야합니다.

 

트랜잭션

정보처리기사 공부를 하다보면 트랜잭션 개념을 만나게 되는데, 여기서 나왔던 설명이 괜찮게 느껴져서 인용해겠습니다.

 

1. A라는 사람이 B의 중고 물건이 마음에 들어서 거래를 진행하게 되었습니다.

2. A는 B에게 물건에 해당하는 금액을 어플을 통해 송금하였습니다.

3. 송금을 했기때문에 A계좌에서는 해당하는 금액만큼 돈이 빠져나갔습니다.

4. 그사이 B계좌로 돈이 입금되어야하지만 알 수 없는 오류가 발생합니다.

5. A는 계좌에서 금액이 빠져나갔지만 B는 돈을 못받는 현상을 막기 위해 트랙잭션이 발생하고, A의 계좌는 원래대로 돌아옵니다.


이런식으로 데이터를 처리하는데 오류나 다양한 상황에 대하여 안정성을 확보하고 성공한 경우에만 반영을 해주는 것을 트랜잭션이라고 합니다.

 

 

 

트랜잭션의 특징

트랜잭션은 4가지의 특징을 지닙니다.

-원자성, 일관성, 독립성, 지속성

 

1. 원자성(Atomicity)

DB에 모두 반영하거나 반영되지 않는 것을 말합니다. 즉, 모두 성공으로 처리하거나 모두 실패로 처리해야합니다.

 

2. 일관성(Consistency)

트랜잭션 작업 처리의 결과가 항상 일관되어야 함을 말합니다.(데이터 타입이 반환 후와 전이 동일해야 함)

 

3. 독립성(Isolation)

동시 발생하는 트랜잭션들이 서로에게 영향을 미치지 않아야합니다.

 

4. 영속성(Durability)

트랜잭션을 성공적으로 마치면 결과가 영구적으로 저장되어야 합니다.

 

 

스프링 프로젝트 트랜잭션 설정하기

1. 어노테이션을 통한 설정하기

트랜잭션 설정을 위해서 다양한 방법이 있지만 그 중 어노테이션을 통한 설정 방법을 알아보겠습니다.

먼저 컨텍스트부분에 아래의 한 줄을 추가해주면 간단하게 적용이 가능합니다.

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

 

 

context-transaction.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:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

	<tx:annotation-driven transaction-manager="txManager"/>
	
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
</beans>

이후 트랜잭션을 처리하고 싶은 메소드나 클래스, 인터페이스 등에 @Transactional 어노테이션을 걸어주면 된다.

 

 

XXXService.java

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE)
public HashMap<Object, Object> insertUser(HashMap<Object, Object> params) {
	int result = loginMapper.insertUser(params);
	params.clear();
	params.put("result", result);
	return params;
}

inserUser라는 매퍼를 통해 insert쿼리를 수행하는 도중 오류가 발생하면 트랜잭션이 발생한다.

 

반응형
반응형

웹 개발을 하다보면 서버에서도 유효성 검증을 필수로 하지만 프론트에서 1차적으로 필터링을 해주면 아무래도 서버에 부하도 적어지고 데이터 타입등 유효하지 않은 데이터를 걸러낼수 있어서, 자바스크립트를 통해 필수적으로 ajax나 submit 처리 전에 데이터 검사를 하게 됩니다.

 

 

문자열 검사하기

문자열을 검사하고 싶으면 생각보다 굉장히 간단하게 검사할 수 있습니다.

기존에는 undefined, null, 공백값등을 체크하는 조건문을 길게 쓰기 싫어서 함수를 만들어서 적용하는 방식으로 사용하거나 조건문에 길게 나열하곤 했었는데, 그 모습은 아래와 같습니다.

 

let text;
if(text === undefined || text === null || text === ''){
	alert("문자열이 비었습니다.");
}

하지만 실상 빈값에 대한 유효성 검증은 변수에 !(not 연산자) 하나만 붙여주면 됩니다.

 

 

 

NOT연산자 하나를 통한 유효성 검증

let text = '';
if(!text){
	alert("문자열이 비었습니다.");
}

!undefined //true
!null      //true
!''        //true
!0         //true
!NaN       //true

 

반응형
반응형

잘 납품되었던 프로젝트 뼈대를 가져와서 다른 프로젝트를 또 만들고 만들고... 이렇게 하다보니 이전에 배웠던 스프링 개념도 많이 흐릿해지고 헷갈리게 되는것 같습니다.

시간이 지나면서 기존 개념도 많이 잊어먹었고, 요구사항에 맞게 스프링 설정을 하면서 어려움을 겪는 느낌이 들어 다시한번 공부를 하고 정리를 해보고자 합니다.(기초지만 역시 주기적으로 다시 봐주고 정리할 필요가 있는 것 같습니다.😎)

 

구동순서

1. was(톰캣 등)를 통해 프로젝트를 구동을 하게되면 해당 프로젝트 내부의 web.xml을 로드합니다.

web.xml은 서블릿 배포 기술자로 DD(Deployment Descriptor)라고도 불립니다. WEB-INF 디렉토리에 존재하며 웹 어플리케이션의 설정을 구성하고 있습니다.

webapp-WEB-INF에 위치합니다.

 

 

2. web.xml 파일을 로드하면서 Servlet Container가 구동됩니다.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:egovframework/spring/context-*.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/egovframework/springmvc/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

 

3. Servlet Container가 구동되면서 ContextLoaderListener클래스가 자동으로 메모리에 생성됩니다. 

ContextLoaderListener는 서블릿 이전에 서블릿을 초기화 시켜주며 contextConfigLocation을 설정파일들의 위치를 지정시켜줍니다.

 

4. contextConfigLocation는 공통으로 사용할 의존성 설정파일들을 지정하며, <param-value>부분에 파일 경로를 입력해주면 됩니다.

저는 전자정부프레임워크를 사용하면서 기본 위치가 'egovframework/springmvc/'으로 되어있고, dispatcher-servlet.xml읽도록 설정했습니다.

 

5. dispatcher-servlet.xml을 읽어서 새로운 Spring Container를 생성하고, Controller 객체를 메모리에 올립니다.

 

6. dispatcher-servlet.xml(servlet-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:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:task="http://www.springframework.org/schema/task"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                http://www.springframework.org/schema/task
                http://www.springframework.org/schema/task/spring-task-3.0.xsd">
                
    <task:annotation-driven></task:annotation-driven>

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <context:component-scan base-package="egovframework"/>

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/jsp/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
</beans>

- annotation-driven: xml이 길고 복잡해지면서 대안으로 나타났습니다. 클래스, 메소드 등에 @를 달아 기능을 수행할 수 있도록 동작하기 위해 설정합니다. (xml Bean 등록이 더 우선순위가 높습니다.)

- mvc:annotation-driven: @Controller @RequestMapping 등 컨트롤러에서 해당하는 기능을 설정할 수 있게 해줍니다. 

- task:annotation-driven: @Scheduled 어노테이션을 사용을 위해 처리합니다.(스케줄러 관련)

- org.springframework.web.servlet.view.InternalResourceViewResolver: 컨트롤러가 모델을 리턴하고 DisapatcherServlet이 해당하는 파일을 처리할 때 필요한 정보를 기술하는 태그 접미사가 'WEB-INF/jsp'이면서 '.jsp'로 끝나는 파일들을 찾습니다. 

WEB-INF/jsp/login/login.jsp

- context:component-scan: 특정 패키지의 클래스들을 스캔하여 어노테이션을 확인하고 bean 등록을 한다. @Component @Controller @Service @Repository등이 클래스에 설정해야 등록된다.

 

 

 

 

 

웹 요청 동작순서

1. 사용자가 브라우저를 통해 주소를 입력합니다. - ( http://localhost/login.do )

web.xml에서 설정했던 정보에 맞는 요청일 경우 DispatcherServlet이 해당 요청을 먼저 가로채서 적합한 Controller에 매핑을 해줍니다.

 

2. <servlet-mapping/>부분이 있는데, 설정된 <servlet-name/>에 해당하는 매핑 설정을 할 수 있습니다.

예를들어 <url-pattern/>부분이 '/'으로 처리되어 있다면 모든 요청을 가로챕니다. 하지만 설정부분은 '*.do'로 되어있어서 'login.do'와 같은 형태만 적용이 됩니다.

 

3. 가로챈 매핑정보를 통해 처리할 Controller를 찾은 후, 적용된 코드에 따라 기능이 처리된다.

   3-1. Controller(mapping) -> View

   3-2. Controller(mapping) -> Service -> DAO -> mapper -> DB -> DAO -> Service -> Controller -> Return

 

4. 3-1과 같은 처리가 수행되면 ViewResolver에 의해 해당하는 view를 반환한다.

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/jsp/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>

dispatcher-servlet.xml에 설정된 ViewResolver부분에 맞는 prefix, suffix의 정보를 토대로 view페이지를 제공합니다.

(저는 tiles3를 적용해서 다르게 설정이 되어있습니다.)

@RequestMapping(value = "/login.do")
public ModelAndView login(ModelAndView mv) {
    mv.setViewName("login/login");
    return mv;
}

http://localhost/login.do -> '/WEB-INF/jsp/login/login.jsp'

 

5. 해당하는 viewDispatcherServlet에 보내면 DispatcherServlet은 클라이언트에게 데이터를 전송한다.

이때, 모델의 속성값이 있으면 같이 전송된다. (해당하는 값은 .jsp파일에서 ${변수명}을 통해 사용이 가능하다)

 

 

 

반응형