반응형

스프링 구조에서만 Mybatis를 사용해보고 Maven 프로젝트에서 Mybatis연결은 처음해보게 되었는데, 항상 Autowired와 같은 어노테이션과 설정으로 자동으로 DI(의존성주입)처리가 된 DAO를 사용하다가 이번에 싱글톤 형태로 구성하여 최초에만 연결처리를 하고 연결된 객체를 계속해서 사용할 수 있도록 구성해보았습니다.

 

Mybatis 설정하기

pom.xml

<!-- MyBatis -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.1</version>
</dependency>

<!-- Mysql -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.31</version>
</dependency>

Mysql에 연결하는 예제로 진행됩니다.

 

 

mybatis 연결정보를 담아놓을 xml을 만들겠습니다.

mybatis 패키지를 추가하시고 mybatis-config.xml을 생성합니다.

 

mybatis-config.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="cacheEnabled" value="false"/>
		<setting name="useGeneratedKeys" value="ture"/>
		<setting name="lazyLoadingEnabled" value="true"/> 
		<setting name="defaultExecutorType" value="BATCH"/>
		<setting name="defaultStatementTimeout" value="3000"/>
	</settings>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>  
                <property name="poolMaximumActiveConnections" value="100"/>
                <property name="poolMaximumIdleConnections" value="100"/>
                <property name="poolTimeToWait" value="20000"/>
          </dataSource>
        </environment>
    </environments>
   
    <mappers>
        <mapper resource ="com/psw/exScheduler/mybatis/mapper/board.xml"/>  
    </mappers>
</configuration>

 

 

다음으로 매퍼를 추가합니다. 방금 추가한 mybatis 패키지 안에 mapper라는 패키지를 추가합니다.

board.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="board">
	<select id="selectTest" parameterType="java.util.HashMap" resultType="HashMap">
		select * from board limit 3;
	</select>
</mapper>

간단하게 3개만 board테이블에서 조회해오는 쿼리입니다.

 

 

이제 설정파일들을 읽어들이고 처리할 자바파일을 만들어보겠습니다.

utils라는 패키지를 추가하고 SqlSessionFactorys.java를 생성합니다.

SqlSessionFactorys.java

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Properties;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;

public class SqlSessionFactorys {
	
	private static final Logger logger = Logger.getLogger(SqlSessionFactorys.class);
	
	private SqlSessionFactorys() {}
	
	static SqlSessionFactorys instance;
	
	private static SqlSession sqlSession;
	
	public static SqlSessionFactorys getInstance() {
		if(instance == null) {
			logger.info("::: SqlSessionFactory Connection "+new Date(System.currentTimeMillis())+" :::");
			
			String resource = "com/psw/exScheduler/mybatis/mybatis-config.xml";
			
			Properties props = new Properties();
			props.put("driver"      , "com.mysql.jdbc.Driver");
			props.put("url"         , "jdbc:mysql://127.0.0.1:3306/db_test?useUnicode=true&amp;characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&amp;defaultFetchSize=1000");
			props.put("username"    , "userName"); //각각 db연결 정보에 맞게 입력		
			props.put("password"    , "password"); //각각 db연결 정보에 맞게 입력

			InputStream inputStream = null;
	        try {
	        	inputStream = Resources.getResourceAsStream(resource);
	        	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);
	        	
	        	sqlSession = sqlSessionFactory.openSession(false);
	        	props.clear();
	        }catch(Exception e) {
	        	logger.error(e);
	        }finally {
	        	if(inputStream != null) {
	        		try {
	        			inputStream.close();
	        		}catch(IOException e) {
	        			logger.error(e);
	        		}
	        	}
	        }
		}
		instance = new SqlSessionFactorys();
		
		return instance;
	}
	
	public SqlSession getSqlSession() {
		return sqlSession;
	}
}

싱글톤 형태로 구성하기 위해 생성자는 private처리하여 접근할수 없도록 방지하였고, getInstance 메소드를 통해 최초에 한번만 SqlSessionFactory를 구성하고 담아둘수 있게 구성하였습니다.

 

SqlSession은 연결 후 해당 Class에서 getSqlSession메소드를 통해 가져와서 사용하면 되는 방식입니다.

 

SchedulerTest.java

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.psw.exScheduler.utils.SqlSessionFactorys;

@DisallowConcurrentExecution
public class SchedulerTest implements Job{
	
	private final Logger logger = Logger.getLogger(SchedulerTest.class);
	
	private static final SqlSession session = SqlSessionFactorys.getInstance().getSqlSession();

	public void execute(JobExecutionContext context) throws JobExecutionException {
		logger.info("::: 스케줄러가 동작합니다.  current Time [" + new Date(System.currentTimeMillis()) + "] :::");
		
		//test select db
		List<Map<Object, Object>> list = session.selectList("board.selectTest");
		for(int i=0; i<list.size(); i++) {
			System.out.println(list.get(i).toString());
		}
	}
}

 

동작 확인

작성이 완료되었으니 실제 동작하는지 확인을 해보겠습니다.

App.java

package kr.or.kisa.ktoaInterlock;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

public class App {
	
	private static final Logger logger = Logger.getLogger(App.class);
	
	private static PropertyReader prop = PropertyReader.getInstance();
	
	public static void main(String[] args){
		//SchedulerFactory Create
		SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		try {
			Scheduler scheduler = schedulerFactory.getScheduler();
			
			JobDetail job = newJob(SchedulerTest.class)
					.withIdentity("jobName", Scheduler.DEFAULT_GROUP)
					.build();
			
			Trigger trigger = newTrigger()
					.withIdentity("triggerName", Scheduler.DEFAULT_GROUP)
					.withSchedule(cronSchedule("0 0 11 * * ?"))
					.build();
			
			scheduler.scheduleJob(job, trigger);
			scheduler.start();
		}catch(Exception e) {
			logger.error("Exception", e);
		}
	}
}

실제 db정보와 연결된 mybatis로 가져온 쿼리 정보입니다.

일치하는것을 확인 할 수 있습니다.

반응형
반응형

https://myhappyman.tistory.com/138

 

JAVA - Maven 프로젝트에서 스케줄러 구성하기(Quartz) - 1

고객사의 요청으로 일정시간마다 특정 DB에서 데이터를 가져오고 넣어주는 작업이 필요했습니다. 일정시간마다 동작이 필요하다보니 자바로 스케줄러를 구성하게 되었고 Quartz 를 사용하여 구��

myhappyman.tistory.com

 

1장에 이어 이번엔 스케줄러에 로그처리를 위해 log4j 설정을 진행해보겠습니다.

 

로그는 실제로 서버나 프로세스를 운용을 하면서 많은 정보를 남겨두어 버그나 문제가 발생하였을때, 원인을 분석할 수 있는 아주 유용한 정보가 될 수 있기에 설정과 처리에 따라 아주 유용한 데이터가 될 수도 있지만, 필요없는 모든 출력문구를 다 찍게해놓으면 분석도 힘들뿐더러 용량만 잔득 차지하는 데이터가 될 수도 있습니다.

 

 

로그 설정(log4j)

pom.xml

<!-- log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

먼저 log4j를 추가합니다.

 

log4j.properties

log4j에 사용할 설정파일을 java디렉토리에 생성하고 설정합니다.(패키지 안에 생성이 아닙니다.)

# Root logger option

#log4j.rootLogger=debug, stdout, logfile
log4j.rootLogger=info, stdout, logfile

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p (%C{2}:%L) - %m%n

# Direct log message to log file
log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.DatePattern='.'yyyy-MM-dd
log4j.appender.logfile.File=C:/test/scheduler.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] %5p (%C{2} - %M:%L) - %m%n
log4j.appender.logfile.Append=true

많은 옵션들 중에 log4j.appender.logfile.File 부분에 시스템에 출력할 위치를 지정합니다.

 

저는 C드라이브/test디렉토리에 출력되도록 설정하였습니다.

이후 App.java에 log를 찍어봅니다.

 

App.java

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import org.apache.log4j.Logger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import com.psw.exScheduler.scheduler.SchedulerTest;


public class App {
    private static final Logger logger = Logger.getLogger(App.class);

    public static void main(String[] args) {
        logger.info("Applcation Start!!!");

        // SchedulerFactory Create
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();

            JobDetail job = newJob(SchedulerTest.class)
                    .withIdentity("jobName", Scheduler.DEFAULT_GROUP)
                    .build();

            // 5 seconds action
            Trigger trigger = newTrigger()
                    .withIdentity("trggerName", Scheduler.DEFAULT_GROUP)
                    .withSchedule(cronSchedule("0/5 * * * * ?"))
                    .build();

            scheduler.scheduleJob(job, trigger);
            scheduler.start();
        } catch (Exception e) {
            logger.error(e);
        }
    }
}

이클립스 콘솔창에 정상적으로 출력되는 모습을 볼 수 있습니다.

 

 

해당 디렉토리에도 log파일이 생성되었고 열어보면 이클립스 콘솔창에 찍었던 로그가 정상적으로 쌓인 모습을 볼 수 있습니다.

반응형
반응형

고객사의 요청으로 일정시간마다 특정 DB에서 데이터를 가져오고 넣어주는 작업이 필요했습니다.

 

일정시간마다 동작이 필요하다보니 자바로 스케줄러를 구성하게 되었고 Quartz를 사용하여 구성해보았습니다.

 

spring 프로젝트에서 was에 스케줄러 추가하여 구성하여도 되지만, 이번 경우엔 간단하게 데이터 처리만 하면 되기때문에 Maven 프로젝트로 생성하여 스케줄러를 구성해보았습니다.

 

 

 

JAVA 스케줄러 구성하기

먼저 Maven프로젝트를 생성하시고 pom.xml에 사용할 라이브러리인 Quartz 정보를 추가합니다.

pom.xml

<!-- scheduler - quartz  --> 
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

SchedulerTest.java

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

@DisallowConcurrentExecution
public class SchedulerTest implements Job{

    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("::: 스케줄러가 동작합니다.  current Time [" + new Date(System.currentTimeMillis()) + "] :::");
    }
}

동작시킬 스케줄러를 구성합니다.

 

SchedulerTest.java 파일을 만들고 Job을 상속받습니다. execute()에 동작시킬 내용을 정의합니다.

 

@DisallowConcurrentExecution 어노테이션은 동기화 처리를 위해 추가하였습니다.

어노테이션을 설정한 해당 스케줄러가 끝나지 않은 상태에서 다음 요청이 들어왔을 경우 대기하고 있게 됩니다.

저같은 경우는 5초마다 동작하는 스케줄러를 구성예정입니다. 하지만 5초안에 스케줄러의 동작이 끝나지 않게 되면 DB 데이터가 꼬일 수 있기때문에 Lock처리를 해두었습니다.

 

App.java

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class App{

    public static void main(String[] args) {
        // SchedulerFactory Create
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();

            JobDetail job = newJob(SchedulerTest.class).withIdentity("jobName", Scheduler.DEFAULT_GROUP).build();

            // 5 seconds action
            Trigger trigger = newTrigger().withIdentity("trggerName", Scheduler.DEFAULT_GROUP)
                    .withSchedule(cronSchedule("0/5 * * * * ?")).build();

            scheduler.scheduleJob(job, trigger);
            scheduler.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

실제로 동작시킬 App.java의 메인문을 구성합니다.

 

먼저 동작시킬 스케줄러를 연결해주고 Trigger부분에서 동작시킬 주기를 설정합니다.

동작주기는 Crontab형태로 입력합니다.

저는 매 5초마다 동작하도록 설정하였고, 동작 결과는 아래와 같습니다.

 

동작결과

 

반응형
반응형

checkbox는 자주사용하는 html tag이지만 브라우저별로 약간의 사이즈 차이와 이미지가 차이가 있다보니 css를 통해 통일화를 시키고 싶지만 CSS를 통해 checkbox를 꾸미는것은 불가능하기 때문에 이미지로 대체하여 동작시키는 경우가 많다.

 

label태그를 사용하면 checkbox를 꾸밀 수 있는데 코드는 아래와 같다.

 

checkbox.html

<input type="checkbox" id="myCheck">
<label for="myCheck"></label>

id를 지정해주고 label태그는 바라볼 checkbox와 연결해준다.

 

 

css

input[type="checkbox"]+label {
    display: block;
    width: 24px;
    height: 24px;
    background: url('./images/check-off.png') no-repeat 0 0px / contain;
}

input[type='checkbox']:checked+label {
    background: url('./images/check-on.png') no-repeat 0 1px / contain;
}

input[type="checkbox"] {
    display: none;
}

+를 통해 바로 뒤에 오는 label태그를 같이 선택시킨다.

그런 다음 기본이미지("체크되지 않은 이미지")를 배경으로 처리하고 :checked속성을 추가하여 선택된 이미지("체크 이미지")를 넣는다.

 

마지막에 원래 이미지인 checkbox는 숨긴다.

 

동작정보

이미지로 대체된 기본 체크박스

변경된 체크박스

반응형
반응형

야밤에 잠깐 소스를 봐준다고 팀뷰어로 상대방 개발자분 컴퓨터를 보고있었는데, VS CODE툴을 사용해서 개발중이었다. VS CODE의 open in browser 확장프로그램 기능을 사용중이셨는데... 엉뚱하게 갑자기 해당 확장프로그램이 정상동작을 하지 않는 현상이 발견되었고 옵션을 사용할때마다 계속해서 동일 에러를 출력하였다.

 

문구는 다음과 같았다.

 

Vscode Error : Open browser failed!! Please check if you have installed the browser correctly!

 

여러가지 해결방법도 찾아봤는데 일단 명확한 해결방법을 찾지를 못했고, Stackoverflow 글들을 참고하면서 setting에서 브라우저 지정도 해보고 vscode를 전부 삭제하기전에 설정파일들까지 초기화해서 전부 삭제하고 재설치도 해보고 크롬도 재설치해봤지만, 해결은 되지 않았다.

 

그러다가 찾은 글이 같은 확장프로그램은 아니지만 버전을 낮춰보니 정상동작한다는 글을 보았고, 적용을 하고 리로드하니 정상동작하는것을 보았다.

 

1. 확장프로그램 탭으로 이동하고 원하는 확장프로그램의 톱니바퀴 아이콘을 클릭한다.

 

2. 다른버전 설치를 클릭한다.

 

3. 버전 정보들 중에 원하는 과거버전으로 설치한다.

(해당 문제가 발생하여 1.1.0 버전으로 설치하고 정상 동작을 확인했습니다.)

반응형
반응형

이번 포스팅에선 달력 라이브러리를 하나 다뤄볼까 합니다.

 

air - datepicker라는 녀석이고 jqeury가 필요합니다.

아래는 공식 홈페이지입니다. 다양한 예제와 사용법이 정리되어 있습니다.

http://t1m0n.name/air-datepicker/docs/

 

Air Datepicker

Datepicker's language. If string is passed, then language will be searched inDatepicker.languageobject. If object is passed, then data will be taken from this object directly. If some fields are missing, they will be taken from default localization object

t1m0n.name

 

웹 페이지를 구성하다보면 달력은 굉장이 자주 집어넣게 되는 요소인데, jquery ui의 기본 datepicker 아무래도 너무 단순하고 디자인을 잘 모르는 제가 봐도 족히 10년전쯤에 사용했을 것 같은 디자인이라 꺼려지게 되었는데, air-datepicker는 jquery ui datepicker에 비하면 굉장히 깔끔합니다.

 

색상도 많이 들어가지 않아 깔끔하고 어지간한 페이지에 조화로운 느낌이며 옵션 또한 많은 기능을 제공하고 있어서 큰 문제없이 기능을 제어하고 동작시킬 수 있고, 달력 + 시간조합도 가능하기 때문에 일자와 시분초까지 표현해야 하는 경우에도 유용하게 사용할 수 있습니다.

 

사용법을 알아보겠습니다.

 

Air-datepicker

먼저 동작을 위해 js와 css를 다운받아야합니다.

https://github.com/t1m0n/air-datepicker/tree/master/dist

 

t1m0n/air-datepicker

Cool jQuery datepicker. Contribute to t1m0n/air-datepicker development by creating an account on GitHub.

github.com

개발자님의 github로 가셔서 다운로드 받습니다.

 

귀찮으신분들은 아래의 제가올린 압축파일 받습니다.

datepicker.zip
0.07MB

 

단일 달력

<html>

<head>
    <title>datepicker example</title>
    <script src="./jquery-3.1.1.min.js"></script> <!-- 값 제어를 위해 jquery -->
    <link href="./datepicker/css/datepicker.min.css" rel="stylesheet" type="text/css" media="all">
    <!-- Air datepicker css -->
    <script src="./datepicker/js/datepicker.js"></script> <!-- Air datepicker js -->
    <script src="./datepicker/js/datepicker.ko.js"></script> <!-- 달력 한글 추가를 위해 커스텀 -->
</head>

<body>
    <div>
        단일 달력<br />
        <input type="text" id="datepicker">
    </div><br /><br /><br />
</body>
<script>
    $("#datepicker").datepicker({
    	language: 'ko'
    }); 
</script>

</html>

추가한 input text박스를 클릭하면 달력이 생성되고 원하는 날짜를 입력하면 input태그에 날짜가 들어갑니다.

태그안에 속성값을 추가하여 input text박스를 달력으로 사용하거나 스크립트에 요소를 지정해서 사용할 수 있습니다.

 

* 참고로 한국어 언어가 없을 수 있습니다. 저는 datepicker.ko.js 파일을 따로 만들어서 사용중이고, 해당 파일은 위에서 제가 제공하는 파일을 받으시면 됩니다.

 

 

 

단일 달력에 시간 선택하기

$("#datepicker").datepicker({
    language: 'ko',
    timepicker: true,
    timeFormat: "hh:ii AA"
});

아까와 같은 소스에서 이번엔 timepicker 옵션만 true처리 해주면 시간 선택이 가능해집니다.

여기에 추가적으로 시간이 표현될 표현식을 만들어주면 정상적으로 시간 선택까지 할 수 있습니다.

 

 

 

연결된 달력 만들기(날짜 제한처리)

단일 달력의 경우 그냥 일자를 선택하게 해주면 되는데, 시작일 ~ 종료일형태로 되어있는 경우

종료일이 시작일보다 과거로 가거나 시작일이 종료일보다 미래로 선택되는것은 막아야 할텐데, 옵션을 통해 막도록 설정해보겠습니다.

 

datepicker.js

datePickerSet($("#datepicker1"), $("#datepicker2"), true); //다중은 시작하는 달력 먼저, 끝달력 2번째

/*
    * 달력 생성기
    * @param sDate 파라미터만 넣으면 1개짜리 달력 생성
    * @example   datePickerSet($("#datepicker"));
    * 
    * 
    * @param sDate, 
    * @param eDate 2개 넣으면 연결달력 생성되어 서로의 날짜를 넘어가지 않음
    * @example   datePickerSet($("#datepicker1"), $("#datepicker2"));
    */
function datePickerSet(sDate, eDate, flag) {

    //시작 ~ 종료 2개 짜리 달력 datepicker	
    if (!isValidStr(sDate) && !isValidStr(eDate) && sDate.length > 0 && eDate.length > 0) {
        var sDay = sDate.val();
        var eDay = eDate.val();

        if (flag && !isValidStr(sDay) && !isValidStr(eDay)) { //처음 입력 날짜 설정, update...			
            var sdp = sDate.datepicker().data("datepicker");
            sdp.selectDate(new Date(sDay.replace(/-/g, "/")));  //익스에서는 그냥 new Date하면 -을 인식못함 replace필요

            var edp = eDate.datepicker().data("datepicker");
            edp.selectDate(new Date(eDay.replace(/-/g, "/")));  //익스에서는 그냥 new Date하면 -을 인식못함 replace필요
        }

        //시작일자 세팅하기 날짜가 없는경우엔 제한을 걸지 않음
        if (!isValidStr(eDay)) {
            sDate.datepicker({
                maxDate: new Date(eDay.replace(/-/g, "/"))
            });
        }
        sDate.datepicker({
            language: 'ko',
            autoClose: true,
            onSelect: function () {
                datePickerSet(sDate, eDate);
            }
        });

        //종료일자 세팅하기 날짜가 없는경우엔 제한을 걸지 않음
        if (!isValidStr(sDay)) {
            eDate.datepicker({
                minDate: new Date(sDay.replace(/-/g, "/"))
            });
        }
        eDate.datepicker({
            language: 'ko',
            autoClose: true,
            onSelect: function () {
                datePickerSet(sDate, eDate);
            }
        });

        //한개짜리 달력 datepicker
    } else if (!isValidStr(sDate)) {
        var sDay = sDate.val();
        if (flag && !isValidStr(sDay)) { //처음 입력 날짜 설정, update...			
            var sdp = sDate.datepicker().data("datepicker");
            sdp.selectDate(new Date(sDay.replace(/-/g, "/"))); //익스에서는 그냥 new Date하면 -을 인식못함 replace필요
        }

        sDate.datepicker({
            language: 'ko',
            autoClose: true
        });
    }


    function isValidStr(str) {
        if (str == null || str == undefined || str == "")
            return true;
        else
            return false;
    }
}

datePickerSet라는 함수를 따로 만들어서 사용해봤습니다.

 

첫번째 파라미터는 시작일의 달력요소를, 두번째 파라미터에는 종료일의 달력요소를 넣어주면 됩니다.

3번째 파라미터는 처음 로드하면서 처리하는 값인지 아닌지 구분용입니다. true처리하고 로드하시면 됩니다.

시작일과 종료일자에 따라 제한이 걸리는 모습을 볼 수 있습니다.

반응형
반응형

~.html 파일에 HTML, CSS, JS등으로 웹 페이지를 열심히 만들고 결과물을 전달해주는 과정에서 스프링프로젝트로 들어가는건지 모르겠지만 html파일을 전부 JSP로 변경해서 넘겨달라는 요청사항이 있었다.

 

한번 하는거면 문제가 없지만 넘기고 나서도 추가적으로 변경 요청사항이 있었고 그때마다 매번 html을 작업해보고 jsp로 하나하나 수정하는것도 번거로울 뿐더러 include를 통해 내부에 태그까지 심어달라는 둥 여러가지 요구사항이 있어서 구조적으로도 많이 달라지는 상황이었다.

 

그래서 애초에 jsp에서 다 작업해버리는게 좋다고 판단했고, 주로 사용하던 툴인 VS Code에서 간단하게 JSP를 구동시키고 로컬에서 페이스도 볼 수 있도록 환경 설정을 해보겠다.

 

spring 프레임워크 boot 설정법은 아니므로 해당 방법이 필요한분들은 다른 포스팅을 찾아보셔야 할 것 같습니다.

 

톰캣 설치

일단 먼저 WAS서버가 필요한데, 가장 보편적으로 사용하는 톰캣을 설치하자.

https://tomcat.apache.org/download-80.cgi

 

Apache Tomcat® - Apache Tomcat 8 Software Downloads

Welcome to the Apache Tomcat® 8.x software download page. This page provides download links for obtaining the latest versions of Tomcat 8.x software, as well as links to the archives of older releases. Unsure which version you need? Specification versions

tomcat.apache.org

다운로드가 끝나면 인스톨 프로그램을 통해 설치를 진행한다.

윈도우 환경이라고 생각하고 설치는 간단하니 넘어가도록 하겠다. (설치경로는 기억해야합니다.)

 

 

 

VS CODE 설정

확장 프로그램 설치 - Debugger for Java, Tomcat for Java

먼저 2가지 확장 프로그램을 설치해줘야 한다. 맨 왼쪽 5번째 탭을 누르고 확장 프로그램을 설치해주자.

 

 

2가지 설치가 끝나고 탐색기탭으로 돌아가면 최하단에 TOMCAT SERVERS라는 탭이 생긴 것을 볼 수 있다.

 

탭을 열어보면 아무것도 없을텐데(캡처본은 제가 추가해서 보입니다...)

+버튼을 눌러서 아까 먼저 설치한 톰캣을 등록해준다.

 

아까 설치한 톰캣을 선택해주면 정상적으로 등록된다.

(일반적으로 설치프로그램을 통해 설치하면 C:\Program Files\Apache Software Foundation\Tomcat 8.5

해당 경로에 있을 확률이 높다. 설치경로를 잘 파악해둔다.)

 

톰캣이 정상적으로 등록되고 오른쪽 클릭을하면 여러가지 메뉴를 볼 수 있다.

Start : 톰캣을 구동한다.

Stop : 톰캣을 종료한다.

Restart : 톰캣을 재시작한다.

Rename : 등록한 톰캣의 이름을 변경한다.

Open in Browser : 브라우저를 통해 톰캣을 연다.

Open Server Configuration : 가장 중요하다!! 톰캣의 설정을 변경하는 설정파일이다.

 

Open Server Configuration을 클릭하면 server.xml파일이 열린다.

 

server.xml

이런식으로 xml파일이 보이는데, 우리가 추가해야할 정보는 2가지정도가 있다.

 

Host태그쪽에 보면 Value태그가 있는데 바로위에 Context라는 태그를 추가할것이다.

<Context path="" docBase="JSP작업을 할 위치"></Context>

 

해당 태그를 추가하고 Service 태그 바로 아래 첫번째 Connector의 포트값을 8080 기본에서 80으로 변경해준다.

<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>

 

작업이 끝나면 위 캡처의 형태로 변경되어 있어야 한다.

 

그럼 준비가 끝났다.

 

현재 세팅한 작업폴더의 위치는 E드라이브의 project의 jspWorks이다.

해당 폴더에 jsp를 만들고 확인해보겠다.

 

E:/project/jspWorks/test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>

<head>
    <title>VS CODE에서 JSP 동작시키기</title>
</head>

<body>
    <h1> test.jsp 페이지입니다.</h1>
</body>

</html>

 

test.jsp를 만들고 톰캣서버 탭에서 톰캣을 구동한다.

그럼 OUTPUT 탭에 여러 메시지들이 쭈르륵 출력되고 원하는 브라우저 하나를 켠다.

 

 

http://localhost/test.jsp

jsp명까지 끝까지 입력하여 주소를 입력하고 정상적으로 페이지가 열리는지 확인한다.

 

 

2020-09-25 추가----------------------------------------------------------

구동한 jsp의 .java, .class 위치 지정하기

먼저 확인해보았던 server.xml을 열어봅니다.

 

내부 태그 내용 중 Host태그를 찾고 workDir을 추가합니다.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Server port="8010" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml"/>
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxPostSize="-1" maxParameterCount="-1"/>
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" 
          workDir="배포시킬 경로">
        <Context path="" docBase="E:/project/jspWorks/ROOT"></Context>
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b"/>
      </Host>
    </Engine>
  </Service>
</Server>

톰캣을 재기동 하면 지정한 위치에 .java, .class가 생성된 것을 확인 하실 수 있습니다.

 

반응형
반응형

보통 파일 업로드를 할때 체크하는게 보통 파일의 마지막 '.'위치를 찾아서 확장자를 체크하고 파일의 사이즈정도를 항상 js를 통해 체크하는수준이였는데, 이번에 파일의 위·변조 체크요청이 있어서 찾다보니 파일의 MIME TYPE을 체크하여 위변조를 체크할 수 있는 방법을 확인하였습니다.

Apache Tika를 사용하여 파일의 MIME TYPE을 체크하며, 그전에 MIME TYPE에 대해 간단하게 알아보겠습니다.

 

MIME TYPE

Multipurpose Internet Mail Extensions의 약자로 인터넷에서 파일의 성격과 형식에 따라 파일을 식별하는 방법이다.

 

브라우저의 HTTP 응답에 정의된 Content-type의 헤더값을 확인하면 extension/plugin 이다.

과거 SMTP 프로토콜을 사용하여 전송되는 이메일을 위해 작성되었다.

 

하위 유형 앞에 x-로 시작하는 경우는 표준이 아니며 등록되지 않았다는것을 의미한다.

하위 유형 앞에 vnd가 붙으면 공급업체에 따라 변경된다는것을 의미한다.(msoffice 등)

 

출처: https://www.freeformatter.com/mime-types-list.html

 

zpage.mime.types.title

MIME Types List I have compiled a full list of MIME types using the "mime.types" file of the Apache HTTPD virtual private server. I took the liberty of adding a name/description for each MIME type so that it's clearer what they represent. I have also inclu

www.freeformatter.com

 

MIME TYPE은 이정도로 간단하게 알아보고 Tika를 사용하여 MIME TYPE을 알아보겠습니다.

 

 

Tika

pom.xml

<!-- Tika -->
<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-core</artifactId>
    <version>1.14</version>
</dependency>

dependency에 추가를 해줍니다.

 

 

(테스트 대상이 될 이미지 3개) 해당 파일들을 경로별로 체크해보겠습니다.

 

 

App.java

import java.io.File;
import java.io.IOException;

import org.apache.tika.Tika;

public class App{

    public static void main( String[] args ){
        File file1 = new File("C:/test/ggome.jpg");
        File file2 = new File("C:/test/kor.gif");
        File file3 = new File("C:/test/box.png");
        try {
            System.out.println(checkImageMimeType(file1));
            System.out.println(checkImageMimeType(file2));
            System.out.println(checkImageMimeType(file3));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static boolean checkImageMimeType(File file) throws IOException{
        Tika tika = new Tika();

        String mimeType = tika.detect(file);
        System.out.println(mimeType);
        if(mimeType.startsWith("image")) {
            return true;
        }else {
            return false;
        }
    }
}

각각 파일들을 detect메소드를 통해 처리해보니 각각 파일의 종류와 확장자를 sysout을 통해 찍어주는 것을 볼 수 있습니다.

 

 

위변조 체크해보기

자 그럼 위변조가 목적이었으니 애초에 텍스트였던 파일을 강제로 확장자를 변경해서 체크해보겠습니다.

이런식으로 텍스트 파일을 만들고 확장자를 변경합니다.

 

 

소스를 통해 확인해보겠습니다.

import java.io.File;
import java.io.IOException;

import org.apache.tika.Tika;

public class App{

    public static void main( String[] args ){
        File file1 = new File("C:/test/test.jpg");
        try {
            System.out.println(checkImageMimeType(file1));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static boolean checkImageMimeType(File file) throws IOException{
        Tika tika = new Tika();

        String mimeType = tika.detect(file);
        System.out.println(mimeType);
        if(mimeType.startsWith("image")) {
            return true;
        }else {
            return false;
        }
    }
}

확장자는 jpg이지만 텍스트 타입이라고 나오고 이미지타입이 아니기때문에 false처리된 것을 볼 수 있습니다.

반응형