반응형

일정 시간마다 데이터를 받아와서 코드 테이블을 동기화 시켜야하는 프로세스가 있었다.

1분마다 돌아가도록 cron설정이 되어 있었고, 쿼츠가 동작하면 rest api형태로 데이터를 전달해주고 수신서버는 해당 데이터를 기반으로 delet -> insert 해주는 간단한 프로세스였다.

 

문제는 수신서버의 로직이였는데, insert할 데이터가 복잡하지 않았지만 2천개 가량의 데이터를 집어넣는데 30초가 넘게 소요되었다.

결국 1분마다 처리를 하다보니 코드는 약 20초간만 최신데이터를 쓸 수 있고, 나머지 시간은 제대로 채워지지 않은 데이터를 보게 되는 현상이였다.

 

추가적으로 rest api로 구성된 로직에서 request후 response가 안오면 뭔가 처리가 있어야하지만 에러상황이 발생하게 되었고, request를 요청한곳에서는 무기한으로 기다리는 현상으로 보였다.

rest api의 타임아웃 처리와 스케줄러 동작시간을 1분에서 5분정도로 변경하고 insert로직을 개선해야했다.

 

HttpPost timeout 설정

HttpPost post = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom()
  .setSocketTimeout(10*1000)
  .setConnectTimeout(10*1000)
  .setConnectionRequestTimeout(60*1000)
  .build();

post.setConfig(requestConfig);

RequestConfig 설정을 추가해주었다.

 

 

 

단일 INSERT 문제가 되었던 기존 로직

xxx.java

newList.forEach(data -> {
	try {
		mapper.insertOrgCode(data);
	} catch (Exception e) {
		e.printStackTrace();
	}
});

 

xxx.mapper

<insert id="insertOrgCode" parameterType="hashmap">
    INSERT INTO TB_TC998_CODE (
        GROUP_CD, CD, PARNTS_CD, CD_NM, DEPTH, REG_DATE
    ) VALUES (
         #{GROUP_CD},
         #{CD},
         #{PARNTS_CD},
         #{CD_NM},
         #{DEPTH},
         GETDATE()  
    )
</insert>

아래와같이 sublist를 통하여 잘라서 foreach를 통해 다량 등록을 하도록 변경하였다.

 

개선안

xxx.java

while(newList.size() > 0) {
	try {
		int toIndex = 200;
		if(toIndex > newList.size()) {
			toIndex = newList.size();
		}
		
		//insert시작
		param.put("list", newList.subList(0, toIndex));
		int result = mapper.insertOrgCode(param);
		
		
		if(result == toIndex) {
			//insert끝나면 list 삭제
			for(int j=0; j<toIndex; j++) {
				newList.remove(0);
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

MAX값이 너무 커도 오류가 발생해서 200개로 제한을 두었다.

 

xxx.mapper

<insert id="insertOrgCode" parameterType="hashmap">
	INSERT INTO TB_TC998_CODE (
		GROUP_CD, CD, PARNTS_CD, CD_NM, DEPTH, REG_DATE
	) VALUES
	<foreach collection="list" item="item" separator=" , " >
        (
            #{item.GROUP_CD},
            #{item.CD},
            #{item.PARNTS_CD},
            #{item.CD_NM},
            #{item.DEPTH},
            GETDATE()  
        )
	</foreach>
</insert>

처리 후 동작시간을 비교해보았는데, 로컬에서도 심각하게 차이가 발생했다.

아래는 start, end 시간을 측정해본 콘솔 창이다.

43초대 2초... 심각하게 차이가 발생한다.

 

다량의 insert를 해야하는 경우가 있으면 mybatis foreach 구문을 사용하도록 하자!

반응형
반응형

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

 

분명히 전자정부프레임워크라고 했는데, 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

웹 개발자로 년차가 쌓이고 있는 현 시점에서 자바스크립트의 내부 동작을 이해할 필요가 있었다.
자바스크립트의 내부에서 어떤식으로 돌아가는지에 대한 내용들을 파악해보고 참고한 내용들을 참고하여 작성하는 글이다.


🪄자바스크립트의 변화

과거 웹 페이지의 동작만을 위해 동작하던 자바스크립트는 현대에 와서 많은 변화가 있었는데, 더 이상 웹 페이지만을 위해 존재하지 않는다.
2008년 구글에서 V8엔진을 사용하여 크롬을 출시한다. 이를 시작으로 2009년 노드가 탄생하였고, 서버와 클라이언트, 어플리케이션 개발 등 다양한 분야에서 사용되게 된다.

🔥자바스크립트 엔진

V8은 자바스크립트의 엔진이다. 크롬 브라우저의 엔진이면서, Node.js의 기반이다.
싱글스레드로 동작하면서 메모리힙콜스택으로 구성되어 있다.

메모리 힙(Memory Heap): 원시타입(Primitive type), 객체(Array, Object, Function 등)타입이 선언되면 메모리힙에 할당된다. 사용이 끝나면 자동으로 해제(Garbage collection)된다.
콜스택(Call Stack): 실행해야 할 코드가 쌓인다.

🧙Call Stack

실행할 코드가 쌓인다고 가볍게 설명했는데, 더 자세히 알아보자.
일단 여기서 이름 자체에 Stack이라는 키워드로 어느정도 동작을 유추할 수 있다.
후입선출를 가진 스택은 마지막에 쌓인 데이터가 먼저 출력되는 방식이다.
요청이 들어올 때마다 이 콜스택이라는곳에 쌓아두는데, 아래와 같은 소스가 있다면 어떻게 쌓이고 동작할까?

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

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

동작 처리 설명

  • Stack1: printSum(5)가 push된다.
  • Stack2: printSum(5) 내부에 있는 tenSum(x)가 push된다.
  • Stack3: tenSum(x)가 연산되면서 15를 출력하고 sum이라는 변수에 담아주고 pop이 된다. console.log(sum)이 push된다.
  • Stack4: console.log(sum)이 출력되면서 pop된다.
  • Stack5: printSum(5)가 출력되면서 pop된다.

🔁Event Loop & Task Queue

아래와 같은 코드를 만나면 어떻게 될까?

console.log("start", new Date())
setTimeout(()=>{console.log("setTimeout", new Date())}, 0);
const wakeUpTime = Date.now() + 1000;
while (Date.now() < wakeUpTime) {}
console.log("test1");
console.log("test2");
console.log("test3");
console.log("end", new Date())

위 소스에 대해 설명하자면 아래와 같다

  • 단순 console.log를 실행한다.
  • setTimeout이 동작한다. 동작시간은 0초로 설정하였다.
  • 이후 현재시간의 1초뒤의 시간을 wakeUpTime에 작성하여 while문에서 시간으로 체크한다.
  • 1초동안 강제로 동기화를 시켜서 페이지가 살짝 멈추게 된다.
  • 이후 단순한 console.log들을 출력한다.

예상한 결과가 나오길 빌면서 아래 출력 결과를 공유한다.

setTimeout메소드가 2번째에 있고 동작시간을 0으로 처리했음에도 콜백안에 있는 console.log는 가장 나중에 출력되고 있는걸 볼 수 있다.
심지어 4번째 while문에서 강제로 블로킹 처리를 해서 페이지가 멈추는 현상까지 발생는데 어떻게 이런 결과가 나왔을까?
이런 현상에 대해서는 이벤트 루프, 테스트 큐에 대해 알아야한다.

먼저 테스크 큐콜백 함수들이 대기하는 큐 형태이다.
Queue는 선입선출 구조로 먼저 들어온 것들이 먼저 나간다.
위 예제의 실제 동작을 설명하자면 아래와 같다.

  • 콜스택에 console,log("start")가 push되고 출력되면서 pop된다.
  • setTimeout이 콜스택에 push되고 콜백 내용이 테스크 큐에 등록되면서 pop된다.
  • 0ms가 지나고 이벤트 큐는 콜 스택이 비워질때까지 기다리면서 콜백을 실행시킬 준비를 한다.
  • wakeUpTime의 변수가 메모리 힙에 올라가고 이후부턴 콜스택들이 동작된다...

위와 같은 비동기 처리나 I/O의 작업이 자바스크립트에서 발생하면 콜백함수가 콜스택에서 처리되는게 아니라 별도의 테스크 큐라는 곳에 쌓였다가 이벤트 루프에 의해 실행되기 때문에 뜻밖의 결과를 얻을 수 있다.

⭐브라우저의 내부 환경

자바스크립트의 엔진을 구동하는 환경의 브라우저의 내부 모습은 아래와 같다.

그림에서도 볼 수 있듯이 개발하면서 자주 사용했던 Ajax, setTimeout과 같은 비동기 처리들은 자바스크립트가 해주는게 아니라 Web API영역에 따로 정의되어 있다. Node.JS에서는 동시성을 위해 libuv라이브러리를 도입했는데, 이 libuv라이브러리가 이벤트 루프를 제공한다.

정리하자면 비동기 작업을 하게되면 Node.js의 API가 호출되고, 그러면서 작성된 콜백 함수들은 이벤트 루프에 의해 관리가 되고 실행이 된다.

반응형
반응형

데이터 시각화 작업을 하다보면 다양한 오픈소스와 차트 라이브러리를 사용하게 되는데, 그 중 오픈소스이며 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를 활용한 세계지도 차트

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

반응형
반응형

https://myhappyman.tistory.com/244

 

JAVA - replaceFirst 치환시 주의점(특수문자 문자열 치환)

$user_id 형태로 되어있는부분을 찾아서 해당하는 특정값 'admin' 형태로 치환하는 특정 로직을 구성했는데 replace가 replaceAll처럼 뒷부분까지 동작하는 현상을 발견하였습니다. 😥 replace 정규식으로

myhappyman.tistory.com

 

이전에도  replaceFirst를 사용하여 문서를 읽으면서 차례대로 치환처리를 해주는 서비스를 개발했었는데, 관련하여 최근에 또 이슈가 발생하였다.

 

java.lang.IllegalArgumentException: Illegal group reference 해당 오류가 발생하고 있었는데, 로그부분의 관련된 서비스 로직부분을 보니 이번에도 replace부분이였다.

 

프론트에서 입력한 파라미터값을 가져와서 html을 만드는 서비스였는데, 특정 위치에 치환을 위해 replaceFirst부분을 사용하였고, 입력한 파라미터값에 '$', '\' 값이 존재하면 해당 오류가 발생하고 있었다.

 

자바 정규식에 특수문자 몇 키워드는 특별한 동작을 하도록 설계가 되어있어서 해당 현상이 발생한다.

이외에도 '+', '*', '?', '^' 등의 키워드가 정규식내에서 특정 키워드로 동작을 하는데, 이런 동작을 하지 않도록 처리할 수 있다.

 

java.util.regex.Matcher.quoteReplacement 메소드를 사용하면 되는데, 기존에 파라미터 처리하던 부분을 아래와 같이 수정하여 해결하였다.

 

기존 문제 발생

reportForm = reportForm.replaceFirst("[$]user_name",  param.get("user_name"))


수정 후

reportForm = reportForm.replaceFirst(
				"[$]user_name",  
				java.util.regex.Matcher.quoteReplacement(param.get("user_name"))
             )

 

반응형
반응형

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

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

 

트랜잭션

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

 

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

 

반응형