반응형

form태그 내부에 input태그가 단일로 존재하면 엔터 입력시 submit동작이 이루어지는걸 알고 계셨나요?

 

input, button태그 하나씩 구성된 form태그를 만들고 이벤트를 작성중이였습니다.

button태그 타입도 submit이 아닌 button으로 지정하였고, js에 key event도 작성하지 않았는데, input에 글을 입력하고 엔터키를 누르면 자동으로 submit동작이 발생하였습니다.(저는 ajax를 통해 데이터를 처리하고 싶었습니다!)

 

다음은 문제가 발생한 코드 예제입니다.

<form name="agencyAddForm" action="" autocomplete="off">
    <div class="form-group m-t-10">
        <label class="control-label col-md-2 col-sm-2 col-xs-2">회사명</label>
        <div class="col-md-8 col-sm-8 col-xs-8">
            <input type="text" id="agencySearchName" class="form-control">
        </div>
        <div class="col-md-2 col-sm-2 col-xs-2">
            <button type="button" id="agencySearchBtn" class="btn btn-success btn-sm">찾기</button>
        </div>
    </div>
</form>

(부트스트랩을 사용하였기에 class가 많습니다.)

다른 내용은 볼 필요가 없고 form태그 내부를 보시면 input태그는 단일 한개가 존재하고 있습니다.

form내부에 input태그가 한개만 존재하면 자동으로 submit동작이 발생한다고 합니다.

 

해결방법

- onsubmit="return false;" 속성 추가하기

<form name="agencyAddForm" action="" autocomplete="off" onsubmit="return false;">
	...
</form>

이를 방지하기 위해 form태그 내부에 onsubmit이벤트를 추가하였습니다.

 

그외 방법으로 불필요한 input text태그를 숨기거나, hidden태그등을 넣어도 되지만, submit동작을 사용하진 않을거라 막아두었습니다.

 

 

ps. 신입때도 form태그 내부에 button태그에 type을 지정을 안하고 기본 동작인 submit동작이 되어서 고생했던 기억이 있는데, 역시 기초가 중요한것 같습니다!!!

1. form태그에 input이 하나만 있으면 엔터입력시 자동으로 submit이 동작한다.

2. form태그에 button이 있고 type지정을 안하면 submit이 동작한다.

 

2가지만 기억하시면 시간낭비가 줄어드실거 같습니다😙

반응형
반응형

단일 이클립스에서 프로젝트를 여러개 구성하여 동시에 개발 테스트하는 경우가 있습니다.

 

가정: 프로젝트 A, B를 동시에 구동한 상태(PORT: 8080, 8081)

A에서 로그인을하여 한참 사용하다가 B에서 로그인페이지를 접근하거나 로그인을 진행하면 A가 끊겨버리는 현상이 발생되었습니다.😅

 

로그인시 처리하는 JSESSIONID값이 중복으로 나오면서 다른 SESSION값으로 덮어씌워지게되고 기존 세션을 못쓰게 되는 현상인데 구동한 톰캣들의 JSESSIONID값을 다르게 설정하시면 해결이 됩니다.

 

JSESSIONID 변경하기

Tomcat - config - server.xml에 접근하여 아래 Context 태그 영역을 수정합니다.

server.xml

<Context docBase="singo" path="/" reloadable="true" 
	sessionCookieName="수정할_JSESSIONID"
	source="org.eclipse.jst.jee.server:singo"/>

sessionCookieName="변경할 sessionId"  를 추가합니다.

 

이클립스에서는 Package Explorer영역에서 Servers라는 디렉토리가 있는데 열어보시면 프로젝트마다 생성한 톰캣이 보입니다.

해당하는 톰캣을 열고 server.xml을 열어서 추가하거나 수정합니다.

반응형
반응형

$user_id 형태로 되어있는부분을 찾아서 해당하는 특정값 'admin' 형태로 치환하는 특정 로직을 구성했는데

replace가 replaceAll처럼 뒷부분까지 동작하는 현상을 발견하였습니다. 😥

 

replace 정규식으로 동작

public class App {
	public static void main(String args[]) {
	    String res = "$user_address, $user_address_detail";
	    res = res.replace("$user_address", "서울특별시 구로구");
	    System.out.println(res);
	}	
}

이유는 $로 인한 정규식으로 동작하였고, 첫번째 찾은 문자열만 변경하고 싶었던 저는 다음으로 찾은 메소드는 replaceFirst를 사용하여 처리하였지만, 다음과 같은 현상을 발견하였습니다.

 

replaceFirst 문제소스

public class App {
	public static void main(String args[]) {
		String res = "my name is $name";
		res = res.replaceFirst("$name", "psw");
		System.out.println(res);
	}	
}

치환이 안됨...

replace메소드에서 치환이 잘되던 replaceFirst가 $ 달러 특수기호를 붙이자 동작이 안되었습니다.🤷‍♂️

해결방법으로 JAVA에서 []로 묶어주게되면 해당부분을 강제로 문자열로 인식하게 할 수 있어 해당방법을 통해 해결하였습니다.

 

변경소스

public class App {
	public static void main(String args[]) {
		String res = "my name is $name";
		res = res.replaceFirst("[$]name", "psw");
		System.out.println(res);
	}	
}

치환이 정상적으로 된다.

 

반응형
반응형

개발 도중이나 실운영시에 문제가 발생하면 항상 제일 먼저 확인하는것이 로그파일부터 찾게 됩니다. 오류에 대한 흔적이나 로직상 개발자가 남겨둔 정보가 있기 때문인데, Log4j2 설정에 대해 정리를 해봅니다.

 

XML 위치

먼저 log4j2.xml 파일의 위치는 WEB-INF/classes하위에 위치시킵니다.

(개발 구조에서는 resources 밑에 위치합니다.)

 

파일 내부 구조

xml내부에는 <Configration> 최상위에 위치하고 <Logger>, <Appender>가 존재합니다.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

 

Logger 설정

Logger부분에서는 어플리케이션의 단위별, 수행 단계를 설정합니다.

name, level, additivity 속성들로 이루어져 있습니다.

<Logger name="egovframework" level="DEBUG" additivity="false">
    <AppenderRef ref="console" />
</Logger>

🟧name: 로거의 이름(java.sql, jdbc.sqltiming...)

🟧level: 로그의 레벨(DEBUG, INFO, ERROR, OFF...)

🟧additivity: 중복로깅여부 (true, false)

 

Appender 설정

로그가 출력되는 위치를 설정합니다.

이클립스등에서 콘솔창에 로그가 출력되는 양식이라거나 로그를 파일로 출력하는 위치, 구조등을 설정합니다.

<Console>, <File>, <RollingFile> 등이 주로 사용됩니다.

구조는 아래와 같이 작성됩니다.

 <Appenders>
   <Console name="console" target="SYSTEM_OUT">
	<PatternLayout pattern="%d %5p [%c] %m%n" />
   </Console>
   <RollingFile name="file" fileName="./logs/${date:yyyy}/${date:MM}/dailyLog.log">
   </RollingFile>
 </Appenders>

RollingFile의 경우 FileAppender의 개선된 형태로 특정 크기이상의 파일로 커지면 기존 파일을 백업파일로 변경하고 다시 로깅을 시작합니다.

 

실제로 나중에 파일을 생성하는 RollingFile쪽을 좀 더 살펴 보겠습니다.

🟧name: 로거의 이름입니다. 추후 AddenderRef와 같은 태그에서 참조를 할때 해당 name을 사용합니다.

🟧fileName: 로그 파일의 생성 경로 및 파일 이름을 지정합니다.

(C:/test/dailyout.log로 지정하면 c:/text 디렉토리 내부에 dailyout.log형태로 생성됩니다.)

🟧filePattern: 파일의 생성 패턴입니다. 서버 일자가 변경되거나 특정 크기가 커지는 등 변화가 필요할때 사용되는 옵션입니다. 이를통해 로그 파일들을 구분합니다.

     🔸${date:yyyy}: 금일 년도만 가져옵니다 (ex) 2019, 2020, 2021)

     🔸${date:MM}: 금일 월을 가져옵니다. (ex) 01, 02, 03, 04 ... 11, 12)

     🔸%d{yyyyMMdd}: 금일 일자 조합

 

 

 

 

 

✅아래는 실제로 사용한 Log4j2.xml 구조입니다.

<RollingFile>의 fileNamefilePattern부분에 로그 위치만 지정하시고 각 로거별 레벨을 수정해서 사용하면 됩니다.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
		<Console name="console" target="SYSTEM_OUT">
		    <PatternLayout pattern="%d %5p [%c] %m%n" />
		</Console>
		<RollingFile name="file" fileName="로그 디렉토리 위치/${date:yyyy}/${date:MM}/dailyLog.log"
								filePattern="로그 디렉토리 위치/${date:yyyy}/${date:MM}/dailyLog_%d{yyyyMMdd}.log">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
			<Policies>
				<TimeBasedTriggeringPolicy />
			</Policies>
		</RollingFile>
    </Appenders>
    <Loggers>
		<Logger name="java.sql" level="OFF" additivity="false">  <!--OFF    DEBUG   ERROR   INFO  -->
			<AppenderRef ref="console" />
		</Logger>
		<Logger name="egovframework" level="DEBUG" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<Logger name="jdbc.sqltiming" level="OFF" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<Logger name="org.springframework" level="INFO" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<Root level="ERROR">
			<AppenderRef ref="console" />
			<AppenderRef ref="file"/>
		</Root>
    </Loggers>
</Configuration>

 

반응형
반응형

5+@ 년전쯤 개발된 소스를 추가 개발하게 되었는데, 레거시 버전들로 이루어져 있었고 고객사에서도 버전업이 가능한지에 대한 문의가 와서 버전업을 진행해보았습니다.

하나씩 버전을 변경하고 구동하다보니 아래와 같은 에러들이 발생했는데🤣, pom.xml부분을 차근차근 수정해서 구동하는데 성공하였습니다.

 

ASM ClassReader failed to parse class file - probably ~....

 

버전업을 하면서 발생한 오류들...2

 

구성 버전

 

먼저 pom.xml을 열어서 수정을 진행합니다.

- 스프링 버전을 4.3.22.RELEASE로 올려줍니다.

<properties>
	<spring.maven.artifact.version>4.3.22.RELEASE</spring.maven.artifact.version>
	<egovframework.rte.version>3.9.0</egovframework.rte.version>
	<spring.security.version>3.2.4.RELEASE</spring.security.version>
	<poi.version>3.12</poi.version>
</properties>

비교를 위한 기존 버전

(스프링 시큐리티부분은 딱히 사용하고 있지 않아 그대로 두었다.)

 

 

- Java11로 올리면서 의존성 오류가 발생하여 maven을 추가하였습니다.

<!-- 자바11 이슈 -->
<dependency>
	<groupId>javax.xml.bind</groupId>
	<artifactId>jaxb-api</artifactId>
	<version>2.3.1</version>
</dependency>
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-core</artifactId>
	<version>2.3.0.1</version>
</dependency>
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-impl</artifactId>
	<version>2.3.1</version>
</dependency>
<!-- 자바11 이슈 -->

 

- 자바와 톰캣 버전을 변경합니다.

<pluginManagement>
	<plugins>
		<plugin>
			<groupId>org.apache.tomcat.maven</groupId>
			<artifactId>tomcat8.5-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>
			<configuration>
				<source>1.11</source>
				<target>1.11</target>
				<encoding>UTF-8</encoding>
			</configuration>
		</plugin>
	</plugins>
</pluginManagement>

톰캣 : tomcat7.0-maven-plugin -> tomcat8.5-maven-plugin

자바 : 1.7 -> 1.11

 

이후 maven update -> clean 이후 구동해봅니다.

 

 

 

반응형
반응형

몽고 DB에서 Aggreate를 통해 쿼리를 전달시 특정 컬렉션을 생성하는 이슈가 있었는데,

몬스태쉬가 자꾸 해당 컬렉션을 가져오면서 엘라스틱에도 데이터를 넣는 상태를 확인 할 수 있었다.

추후 용량등 문제가 발생할 수 있을것이라 판단이 되었고 특정 형태로 생성되는 컬렉션의 이벤트는 감지하지 않도록 설정하고 싶어졌다.

 

monstache 설정 파일인 config.toml 파일에 옵션으로 아래의 설정을 하면 특정 컬렉션의 이벤트를 감지하지 않도록 설정 할 수 있다.

https://rwynn.github.io/monstache-site/config/#namespace-exclude-regex

 

Configuration - Monstache

From here you can search these documents. Enter your search terms below.

rwynn.github.io

namespace-exclude-regex 

공식 홈페이지 설명과 같이 몽고DB에 삽입, 수정, 삭제가 일어나도 해당 정규식으로 설정된 컬렉션은 바라보지 않도록 설정할때 해당 옵션을 사용하는것으로 보인다. 아래 설정을 통해 실제 적용에 성공하였다.😁

 

config.toml

namespace-exclude-regex = '^데이터베이스명.tmp*'
direct-read-dynamic-exclude-regex = '^데이터베이스명.tmp*'

(해당 데이터베이스에 tmp로 시작하는 컬렉션은 바라보지 않도록 설정)

 

 

 

반응형
반응형

카카오 주소🏠 찾기 API

 

https://postcode.map.daum.net/guide

 

Daum 우편번호 서비스

우편번호 검색과 도로명 주소 입력 기능을 너무 간단하게 적용할 수 있는 방법. Daum 우편번호 서비스를 이용해보세요. 어느 사이트에서나 무료로 제약없이 사용 가능하답니다.

postcode.map.daum.net

 

해당 공식 홈페이지에 접속해보면 어렵지 않게 연동을 해 볼 수 있습니다.

요즘 많은 사이트들의 주소 입력창을 보면 아래 캡처와 같은 형태를 자주 접할 수 있었는데, 많은 사이트에서 활용 중인것 같습니다.🙆‍♂️

카카오 주소찾기 양식

 

카카오는 카카오 발송, 지도 API 등 다양한 서비스를 제공하는데 사용을 위해 키 발급, 일일 사용량 제한 또는 용도에 따른 유료처리 등 이슈가 존재합니다.

하지만, 주소 API는 조금 다르게 제공이 됩니다. 👀

아래는 제공되는 형태와 제한에 대한 내용입니다.

https://postcode.map.daum.net/guide

덕분에 연동방법만 익히면 굉장히 쉽게 사용할 수 있습니다. (뭐 거의 준비물이 외부망이 되는 환경이면 끝이네요! 😎)

 

사용법은 아래와 같습니다.

<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
    new daum.Postcode({
        oncomplete: function(data) {
            // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
            // 예제를 참고하여 다양한 활용법을 확인해 보세요.
        }
    }).open();
</script>

 

카카오 주소찾기 적용 예제🐱‍🏍

<!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>Kakao API</title>
</head>
<body>
    <table>
        <tr>
            <th>이름</th>
            <td><input type="text" name="user_name"></td>
        </tr>
        <tr>
            <th>주소</th>
            <td><input type="text" id="address_kakao" name="address" readonly /></td>
        </tr>
        <tr>
            <th>상세 주소</th>
            <td><input type="text" name="address_detail" /></td>
        </tr>
    </table>
</body>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
window.onload = function(){
    document.getElementById("address_kakao").addEventListener("click", function(){ //주소입력칸을 클릭하면
        //카카오 지도 발생
        new daum.Postcode({
            oncomplete: function(data) { //선택시 입력값 세팅
                document.getElementById("address_kakao").value = data.address; // 주소 넣기
                document.querySelector("input[name=address_detail]").focus(); //상세입력 포커싱
            }
        }).open();
    });
}
</script>
</html>

테스트 예시.gif

 

반응형
반응형

구동중인 프로젝트의 특정 경로에서 데이터를 가져와서 추가 처리가 필요한 상태였는데, 서버의 구동중인 절대 경로를 가져오기 위해 아래의 구문을 사용해보니 deprecated처리가 되어있습니다.🙄

@RequestMapping(value = "/report.do",method = RequestMethod.POST)
public @ResponseBody HashMap<Object, Object> report(@RequestParam HashMap<Object, Object> param,
													HttpServletRequest request) {
	String absolutePath = request.getRealPath(request.getContextPath()); //deprecated
	return param;
}

 

문서를 찾아가보니 ServletContext.getRealPath로 대체한다고 되어있습니다.😃

 

request.getSession().getServletContext().getRealPath("/");

 

아래처럼 변경된 문법을 통해 절대 경로를 처리할 수 있습니다.

@RequestMapping(value = "/report.do",method = RequestMethod.POST)
public @ResponseBody HashMap<Object, Object> report(@RequestParam HashMap<Object, Object> param,
														HttpServletRequest request) {
	String absolutePath = request.getSession().getServletContext().getRealPath("/");
	return param;
}
반응형