-->

[JBoss & Wildfly] System property 세팅 및 사용

 

standalone.xml 파일을 열어서 다음과 같이 수정해준다.

<?xml version='1.0' encoding='UTF-8'?>

<server xmlns="urn:jboss:domain:4.2">
    <extensions>       
        ...
    </extensions>
    
    <!-- 시스템 프로퍼티 추가-->
    <system-properties>        
        <property name="server.type" value="dev"/>
    </system-properties>
    
    ...
</server>

 

WAS를 기동시 키고 java단에서 System.getProperty("server.type") 함수를 사용하면 "dev" 값이 나온다.

 

https://kb.novaordis.com/index.php/WildFly_System_Properties#Standard_System_Properties

 

WildFly System Properties - NovaOrdis Knowledge Base

Internal Overview System properties can be declared in the server's configuration files and are propagated to the server JVMs and implicitly to applications. For more details on how system property can be declared, see Declaration in Configuration Files be

kb.novaordis.com

 

[PostgreSQL] 랜덤쿠폰번호 생성 및 중복체크 Function 만들기

 

개발 중인 프로젝트에서 쿠폰 시스템을 도입하게 되어 기존에 다른 개발자 분이 만들어 둔 sql Function을 plpgsql로 수정하여 보완하였다.

 

쿠폰 생성 조건 

1. 쿠폰번호는 16자리

임시 테이블의 칼럼을 varchar(16)으로 생성하였다. 임시 테이블은 세션이 종료되면 자동으로 drop 되기 때문에 function을 이용할 때 사용하기 좋다. 자릿수는 입맛에 따라 바꿀 수 있다.

 

2. 영문과 숫자를 이용한 쿠폰번호 생성

이 부분에서 조금 헤맸다.

기존에 generate_series() 함수를 이용해 필요한 쿠폰 갯수만큼의 random() 숫자를 만들어 사용하였는데..

영문자를 넣기 위해서 다양한 방법을 찾던중에 난수에 md5() 함수로 암호화 하는 신박한 내용을 벤치마킹했다.

영문자는 upper() 함수로 대문자로 만들어 유저가 쿠폰번호를 입력할 때도 자동으로 대문자로 치환되도록 했다.

 

3. 맨앞 4자리는 조직 코드를 이용해 카테고리화

커스터마이징 할 때는 concat() 함수를 이용해서 function 의 return 타입으로 꼭 캐스팅을 해주어야 한다.

 

4. 기존에 쿠폰 테이블에 등록된 번호인지 중복 체크하는 로직이 필요

기존 Function은 아예 쿠폰발행갯수를 2배로 난수를 만들어 최종 return select 쿼리에 limit로 개수를 뽑아내는 방식을 이용하였는데 쿠폰번호가 쌓이게 되면 중복을 피할 수 없는 구조였다. 

이에 loop 문을 이용해 검증 후 임시테이블에 담아둘 수 있도록 했다. 

 

기존 Function

CREATE OR REPLACE FUNCTION ming.fn_coupon_no_generator(as_cnt integer)
 RETURNS TABLE(coupon_no text)
 LANGUAGE sql
AS $function$
		select x1.coupon_no 
		  from (
				select trim(to_char(random() * 10000000000000000, '0000000000000000')) as coupon_no
				  from generate_series(1, as_cnt * 2) 
				except
				select coupon_no 	as coupon_no
				  from ming.coupon_table 
				 where coupon_no is not null   
		       ) x1
		limit as_cnt  
$function$
;

 

최종 쿠폰생성 Function

CREATE OR REPLACE FUNCTION MING.FN_COUPON_NO_GENERATOR(P_ORGANIZATION_CODE CHARACTER VARYING, P_CNT INTEGER)
 RETURNS TABLE(COUPON_NO CHARACTER VARYING)
 LANGUAGE PLPGSQL
AS $FUNCTION$
/* 변수 정의 */
DECLARE
	V_ORGANIZATION_CODE VARCHAR(4) := COALESCE(P_ORGANIZATION_CODE, '5555'); --조직코드 기본값
	V_RANDOM_NO VARCHAR(12) := ''; --쿠폰번호의 랜덤 12자리
	V_COUPON_CNT INTEGER := 0; --만들어진 쿠폰 갯수
BEGIN
	/* 임시 테이블 생성 */
	DROP TABLE IF EXISTS TEMP_COUPON_TABLE;
	CREATE TEMP TABLE TEMP_COUPON_TABLE(
	    COUPON_NO VARCHAR(16)
	);

	/* 랜덤 번호 생성 후 INSERT */
	LOOP 
		INSERT INTO TEMP_COUPON_TABLE 
		SELECT X1.COUPON_NO
		  FROM  (
			SELECT CONCAT(V_ORGANIZATION_CODE, UPPER(SUBSTRING(MD5(RANDOM()::VARCHAR),1,12))) AS COUPON_NO
			EXCEPT
			SELECT T.COUPON_NO 	AS COUPON_NO
			  FROM MING.COUPON_TABLE T
			 WHERE T.COUPON_NO IS NOT NULL   
			   AND T.COUPON_NO != ''		
			) X1;
		V_COUPON_CNT = V_COUPON_CNT + 1;  
		EXIT WHEN V_COUPON_CNT = P_CNT; --최종 카운트에 도달하면 반복문 종료
	END LOOP;
	
	RETURN QUERY SELECT X.COUPON_NO FROM TEMP_COUPON_TABLE X  LIMIT P_CNT;  
END;
$FUNCTION$
;

 

Alias를 잘 먹여야 한다.. function 유효성검사가 너무 예민하다..

 

[Javascript] Input 필드에 공백 제거하기

 

꼭 필요할 때 없는 경우들이 있으므로 블로그에 메모를 해야 한다.

<script>
function noSpaceForm(obj) { // 공백사용못하게
    var str_space = /\s/;  // 공백체크
    if(str_space.exec(obj.value)) { //공백 체크
        //alert("해당 항목에는 공백을 사용할수 없습니다.\n\n공백은 자동적으로 제거 됩니다.");
        obj.focus();
        obj.value = obj.value.replace(/\s| /gi,''); // 공백제거
        return false;
    }
}
</script>

<input type="text" placeholder="아이디 입력" onkeyup="noSpaceForm(this);" onchange="noSpaceForm(this);" />

 

정규표현식으로 표시된 공백을 전부 제거할 수 있다. 

PC유입은 그럴 일이 별로 없긴 한데 모바일 유입은 본인 모르게 공백이 들어가는 경우가 있는 듯..

[PostgreSQL] DB내에 개행과 작은따옴표(') 사용

 

Opensource 기반 툴 사용을 생활화하자!

Postgre 사용하다 보면 개행과 작은따옴표 사용하는 방법을 자꾸 까먹게 되는데, 나 같은 사용자에게 도움이 되라고 적어둔다.

parameter 안에 value값으로 작은 따옴표를 사용하려면 syntax error가 발생하는데 parameter구분을 작은따옴표를 사용하기 때문.. 

 

정상적인 파일이 아닙니다. 'FAQ' 메뉴를 확인해주세요. 라는 메시지를 출력하려면 작은따옴표를 2번 사용하면 된다.

update code_table
   set value = '정상적인 파일이 아닙니다. ''FAQ'' 메뉴를 확인해주세요.'
 where id = 'error_msg'

 

개행을 하기 위해서는 엔터만 쳐도 사실 되고

select 'test line 1
test line 2';

E'{value}'로 묶고 그 안에 \n 개행 문자를 삽입하면 된다.

update code_table
   set value = E'정상적인 파일이 아닙니다.\n''FAQ'' 메뉴를 확인해주세요.'
 where id = 'error_msg'

 

 

 

 

<출처>

https://stackoverflow.com/questions/36028908/postgresql-newline-character

 

PostgreSQL newline character

How to use newline character in PostgreSQL? This is an incorrect script from my experiment: select 'test line 1'||'\n'||'test line 2'; I want the sql editor display this result from my script ab...

stackoverflow.com

PostgreSQL에 대한 설명은 아래 웹페이지로 대체

https://d2.naver.com/helloworld/227936

 

근데 왜 로고가 코끼리야?

http://www.pgsql.ru/db/mw/msg.html?mid=1238939 

 

pgsql.ru: Re: [HACKERS] PostgreSQL logo.

 

www.pgsql.ru

 

[JSP] <jsp:include>와 <jsp:param>

 

웹사이트 구축 열심히 하고 있다.. jsp에는 <jsp:include> 액션 태그를 이용해서 특정 jsp를 import 시킬 수 있다.

html 레이아웃의 Left 영역에 해당하는 부분의 소스를 추가할 수도 있고, 각종 <script> 들을 넣은 리소스 파일을 추가할 수도 있다.

이는 모듈화를 꾀할 수 있는 좋은 방법이 된다.

 

해당 include 된 jsp는 request 객체를 공유한다.

<jsp:include page="../test.jsp" flush="true" />

flush 속성은 포함 대상 JSP 페이지에 제어를 넘기기 직전에 출력 버퍼를 클라이언트로 출력하는 flush를 수행할지 여부(true, false)를 지정한다. 기본값은 false이며, flush 하지 않는다고 한다.

 

여기에 <jsp:param> 태그를 이용해서 공유된 request 객체 안에 parameter를 넣을 수가 있다.

<jsp:include page="../test.jsp" flush="true">
	<jsp:param name="myParam" value="test" />
</jsp:include>

단, <jsp:param>은 String타입의 값만 전달할 수 있기 때문에 형변환이 필요로 하면, request.setAttribute를 이용해야 한다.

그럼 include 된 test.jsp 안에서 스크립틀릿을 이용해 parameter 정보를 가져올 수 있다.

<%
	String strParam = (String)request.getParameter("myParam");
%>

...

<body>
	<div>
		param : <%=strParam%>
	</div>
</body>

 

그러나.. 이렇게 request 객체에서 받아온 정보를 바로 뿌려주는 것은 XSS 보안 이슈를 야기시킨다. 

해커가 request 안의 parameter정보를 '<script>alert('test')</script>' 라고 세팅해서 보내버리면 스크립트가 실행이 되어버리는 것이다. 

따라서 이를 해결하기 위한 방법으로는

1. 파라미터 정보를 일일히 replaceAll 하는 방법

<%!
	public String requestReplace (String paramValue, String gubun) {
        String result = "";
        
        if (paramValue != null) {
        	
        	paramValue = paramValue.replaceAll("<", "&lt;").replaceAll(">", "&gt;");

        	paramValue = paramValue.replaceAll("\\*", "");
        	paramValue = paramValue.replaceAll("\\?", "");
        	paramValue = paramValue.replaceAll("\\[", "");
        	paramValue = paramValue.replaceAll("\\{", "");
        	paramValue = paramValue.replaceAll("\\(", "");
        	paramValue = paramValue.replaceAll("\\)", "");
        	paramValue = paramValue.replaceAll("\\^", "");
        	paramValue = paramValue.replaceAll("\\$", "");
        	paramValue = paramValue.replaceAll("'", "");
        	paramValue = paramValue.replaceAll("@", "");
        	paramValue = paramValue.replaceAll("%", "");
        	paramValue = paramValue.replaceAll(";", "");
        	paramValue = paramValue.replaceAll(":", "");
        	paramValue = paramValue.replaceAll("-", "");
        	paramValue = paramValue.replaceAll("#", "");
        	paramValue = paramValue.replaceAll("--", "");
        	paramValue = paramValue.replaceAll("-", "");
        	paramValue = paramValue.replaceAll(",", "");
        	
        	if(gubun != "encodeData"){
        		paramValue = paramValue.replaceAll("\\+", "");
        		paramValue = paramValue.replaceAll("/", "");
            paramValue = paramValue.replaceAll("=", "");
        	}        	
        	result = paramValue;
            
        }
        return result;
  }
%>

 

2. web.xml에 filter-chain을 이용하는 방법

이건 라이브러리도 다양하다.

 

3. 간단하게 JSTL의 <c:out> 태그를 이용하는 방법

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%
	String Param = (String)request.getParameter("myParam");
	pageContext.setAttribute("strParam", Param);
%>

...

	<div>
    		param : '<c:out value="${pageScope.strParam}"/>'
	</div>
...

나는 이 방법을 택했는데 jstl의 <c:out> 태그는 첫 번째 방법인 replaceAll과 비슷하게 기본적으로 excapeXML이라는 속성이 true로 설정되어 있다.

<jsp:param> 은 request 객체를 공유한다고 했으니 이를 받아오기 위해 PageScope 단위의 변수를 만들어주고 html내부에서는 EL태그로 가져오면 되는 것이다.

 

 

JSP를 요새 중점적으로 다루면서 많은 것을 공부하고 있다. 

그러면서 javascript가 얼마나 강력한 언어인지 다시 한번 알게 된다.

그렇지만 유저가 편해질수록 보안은 강해져야 한다.

 

 

 


<출처>

https://dololak.tistory.com/493

 

[서블릿/JSP] 액션 태그 사용법. 으로 데이터 넘기기. 데이터 공유하기

이전글 [서블릿/JSP] 액션 태그 동작 방식 및 JSP 모듈화 개념 액션 태그 사용법 액션 태그를 사용하면 현재 JSP 페이지에 대상 JSP 페이지의 처리 결과를 포함시킬 수 있습

dololak.tistory.com

http://www.devkuma.com/books/pages/1154

 

프로그램 개발 지식 공유, devkuma

데브쿠마는 프로그래밍 개발에 대한 지식을 공유합니다.

www.devkuma.com

https://lee-mandu.tistory.com/450?category=633568 

 

(공통처리)웹취약성 크로스사이트 스크립트(XSS) 처리하기2_filter chain

안녕하세요. 앞서 크로스사이트 스크립트(XSS)를 처리하기 위하여 포스팅한적이 있습니다. 이는 간단한 경우에 임시로 처리하기 위함이었습니다. [개발/web, html5,jsp] - 웹취약성 크로스사이트 스

lee-mandu.tistory.com

 

[Javascript] Naver 로그인 버튼 커스텀하기

 

요새 웹사이트 구축 프로젝트를 맡고 있는 와중에 핫한 SNS를 통한 간편 회원가입 / 로그인 기능을 구현하고 있다.

네이버, 카카오, 구글 세 가지 플랫폼을 적용하는 게 목푠데 첫 번째부터 난관에 봉착했다.

네이버 녀석들 쓰기가 굉장히 까다롭다는 점...!

 

심지어 네이버 측에서 제공하는 가이드, 샘플코드는 구버전 라이브러리를 사용하고 있다. (1.0.3)

https://developers.naver.com/docs/login/web/web.md

 

Web 애플리케이션 - LOGIN

네이버 아이디로 로그인은 서버 사이드 언어인 PHP나 Java로 개발한 웹 애플리케이션에도 적용할 수 있습니다. 또한 프런트엔드에서 사용하는 JavaScript를 사용해도 적용할 수 있습니다. API 호출 예

developers.naver.com

내가 지금 적용한 최신버전은 무려 2.0.2.... 부들....

결국 라이브러리 소스를 뜯어서 분석하는 초유의 사태가 발생 중이다...

따라서 분석하며 내가 까먹을까봐, 그리고 혹여나 다른 사람에게 미약하게 도움을 줄 수 있을까 해서 카테고리를 신설했다.

 

오늘의 첫 이슈는 바로 네이버 로그인 버튼이 커스텀이 안된다는 것....!

내가 만든 button 태그 혹은 a태그에 네이버 로그인 팝업창을 오픈할 수 있도록 클릭 이벤트를 만드는 것인데

네이버 라이브러리가 하는 짓을 보아하니 id값이 naverIdLogin 컴포넌트에 a 태그를 달고 그 안에 img 태그를 달아 버튼을 만들어 버린다.

따라서 html을 이렇게만 만들고 라이브러리를 적용한 뒤에

<script type="text/javascript" src="https://static.nid.naver.com/js/naveridlogin_js_sdk_2.0.2.js" charset="utf-8"></script>

<div id="naverIdLogin"></div>

<script type="text/javascript">
	var clientId = "내클라이언트ID"
    var callbackUrl = "내콜백URL"
    
    ...
    
	var naverLogin = new naver.LoginWithNaverId({
		 clientId: clientId,
		 callbackUrl: callbackUrl,
		 isPopup: true,
		 loginButton: {color: "green", type: 3, height: 50}		 
	});
</script>    

DOM 작업이 끝나고 소스를 보면

...
<div id="naverIdLogin">
	<a id="naverIdLogin_loginButton" href="#">
		<img src="https://static.nid.naver.com/oauth/big_g.PNG?version=js-2.0.1" height="50">
	</a>
</div>
...

이렇게 만들어진다는 것..^^

 

해결법은 아주 간단하다.

naverIdLogin 이라는 id값을 가지는 태그를 display:none 해버리는 것이다.

그리고 Jquery를 이용해 내가 커스텀할 새로운 태그를 붙여서 클릭 이벤트 시 naverIdLogin의 자식 element 클릭이벤트로 연결만 하면 된다.

<script type="text/javascript" src="https://static.nid.naver.com/js/naveridlogin_js_sdk_2.0.2.js" charset="utf-8"></script>

...

<!-- sns login -->
  ...
  <div id="naverIdLogin" style="display: none;"></div>
  <a href="#" id="naverLogin" class="btn sns-naver" title="새창">네이버로 로그인</a>
  <a href="#" id="kakaoLogin" class="btn sns-kakao" title="새창">카카오로 회원가입</a>
  <a href="#" id="googleLogin" class="btn sns-google" title="새창">구글 회원가입</a>
  ...
<!-- //sns login -->

<script type="text/javascript">
	
    ...
	var naverLogin = new naver.LoginWithNaverId({
		 clientId: clientId,
		 callbackUrl: callbackUrl,
		 isPopup: true,
		 loginButton: {color: "green", type: 3, height: 50}
		 
	});
	
	naverLogin.init(); //initialize Naver Login Button
	 
	$(document).on("click", "#naverLogin", function(){ 
		var btnNaverLogin = document.getElementById("naverIdLogin").firstChild;
		btnNaverLogin.click();
	});
	 
	...
</script>

 

간단하지만 고생하실 분이 있을까 이렇게 남겨놓는다.

 

 

참고로 네이버 디벨로퍼 개발문서를 확인해보면 이렇게 말한다.

 

2.4 로고 이미지, 애플리케이션 이름 확인 

애플리케이션에 등록된 로고 이미지와 애플리케이션 이름이 정상적인지 확인합니다. 로고 이미지가 없거나 네이버가 연상되는 로고를 등록한 경우, 애플리케이션 이름이 네이버를 연상시키거나 비정상적인 경우(예: 애플리케이션 이름을 ‘네이버 로그인’ 또는 ‘로그인’ 등으로 입력) 사전 검수에서 반려됩니다.

로고 이미지와 애플리케이션 이름은 ‘네아로 정보제공동의 화면’에 노출되어 서비스의 브랜드를 나타내는 항목이며, 이용자 입장에서는 정보 제공 동의 전에 자신의 정보가 어떤 사업자에게 전달될 예정인지 식별할 수 있도록 돕는 중요한 정보입니다.

따라서 애플리케이션 설정 시 로고 이미지와 이름은 반드시 서비스 브랜드를 대표할 수 이미지와 이름으로 입력해 주세요.

https://developers.naver.com/docs/login/verify/verify.md#2-4-%EB%A1%9C%EA%B3%A0-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%A6%84-%ED%99%95%EC%9D%B8

 

네아로 사전 검수 가이드 - LOGIN

네아로 사전 검수 가이드 사전 검수가 어렵다면 이것만은 꼭 확인해 주세요 확인 항목 설명 1. 서비스 운영에 반드시 필요한 정보의 조회 권한만 선택했나요? ‘이용자 고유 식별자’ 외에 추가

developers.naver.com

 

 

로그인 버튼 사용 가이드 

네이버 아이디로 로그인은 애플리케이션에 사용할 수 있는 네이버 로그인 버튼 기본 이미지를 제공합니다. 애플리케이션의 상황에 맞게 버튼 이미지의 디자인을 변경할 수 있지만 네이버 고유의 아이덴티티를 유지할 수 있도록 이 가이드에 제시된 디자인을 최대한 유지하는 것을 권장합니다.

https://developers.naver.com/docs/login/bi/bi.md

 

로그인 버튼 사용 가이드 - LOGIN

네이버 아이디로 로그인은 애플리케이션에 사용할 수 있는 네이버 로그인 버튼 기본 이미지를 제공합니다. 애플리케이션의 상황에 맞게 버튼 이미지의 디자인을 변경할 수 있지만 네이버 고유

developers.naver.com

 

변경할 수 있다고 했으면서 막아버린 당신들은 대체...^^

어버이날은 맞아 실기시험 보고 왔습니다 ^^

 

리눅스마스터 1급 실기시험은 총 16문제이고, 1~10번은 단답식, 11~16번은 작업식으로 구성되어 있습니다.

단답식 : 10문제 X 4점 = 40점

작업식 :  6문제 X 4~12점 = 60점

합격 기준은 합계 60점 이상입니다.

 

 

<단답식 1~10>

1. 다음은 커널 컴파일을 진행하는 과정의 일부이다. 조건에 맞게 (괄호) 안에 알맞은 내용을 적으시오.

가. 커널 컴파일을 실행한 적이 있어서 관련 정보를 초기화하고, configure 작업을 통해 생성된 오브젝트 파일 뿐만 아니라 config 파일과 다양한 백업 파일 등도 제거한다.
 # make (      ①      )

나. 커널 컴파일 관련 옵션 설정 작업을 텍스트 메뉴 기반에서 커서를 이용하려고 한다.
 # make (      ②      )

다. 선택한 정보를 기반으로 커널 이미지를 생성한다.
 # make (      ③      )

라. 선택한 커널 모듈을 생성한다.
 # make (      ④      )

■ 조건
 - ①번부터 ④번에는 관련 명령어만 기입한다.

 

 

2. 다음은 2개의 하드디스크를 이용해서 스트라이핑(striping) 기술이 적용된 RAID를 구성하는 과정이다. ( 괄호 ) 안에 알맞은 내용을 적으시오.

 # (      ①      ) (      ②      ) /dev/md0 (      ③      ) /dev/sdb1 /dev/sdc1

■ 조건
 - 2개의 분할된 파티션인 /dev/sdb1, /dev/sdc1에 스트라이핑(striping) 기술을 적용해서 /dev/md0 이라는 RAID 장치로 생성한다.
 - ①번은 관련 명령어만 기입한다.
 - ②번은 ①번 명령어의 옵션 하나만 기입한다.
 - ③번은 ①번 명령어의 옵션을 기입하는데, 여러 옵션과 인자 값이 필요한 경우에는 한 번에 기입한다. (예: -g ihd -d /home/ihd/lin)
 - ①번에 기입되는 명령어가 틀리면 ②번과 ③번은 채점하지 않는다.

 

 

3. 다음은 사용자 추가 시에 생성되는 홈 디렉터리의 경로 및 기본 셸을 변경하고 확인하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. 사용자 추가 시에 생성되는 홈 디렉터리를 /home2 디렉터리이ㅡ 하위 디렉터리가 되도록 변경한다.
 # (      ①      ) (      ②      )

나. 사용자 추가 시에 부여되는 기본 셸을 /bin/csh가 부여되도록 변경한다.
 # (      ①      ) (      ③      )

다. 해당 설정의 변경 여부를 확인한다.
 # cat (      ④      )

■ 조건
 - ①번은 관련 명령어만 기입한다.
 - ②번과 ③번은 명령어의 옵션 또는 옵션과 관련된 인자값을 기입하는데, 명령어를 제외한 나머지 부분을 하나의 괄호로 처리한다. (예: -d /home/ihduser -g kait)
 - ②번과 ③번의 경우 명령어가 틀리면 채점하지 않는다.
 - ④번은 관련 정보가 들어있는 파일명을 절대경로로 기입한다.

 

 

4.다음은 파일 시스템을 점검하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. XFS 파일 시스템인 /dev/sdb1을 점검한다.
 # (      ①      ) /dev/sdb1

나. EXT4 파일 시스템인 /dev/sdc1을 점검한다.
 # (      ②      ) /dev/sdc1

■ 조건
 - ①번과 ②번은 특별한 옵션이나 인자값 없이 관련 명령어만 기입한다.

 

 

5. 다음은 프로세스 우선순위와 관련된 작업을 진행하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. 현재 사용중인 셸의 NI 값을 출력한다.
 # (      ①      )

나. 프로세스 아이디(PID)가 1222번인 프로세스의 NI값을 -10으로 설정한다.
 # (      ②      ) (      ③      ) 1222

■ 조건
 - ①번과 특별한 옵션이나 인자값 없이 관련 명령어만 기입한다.
 - ②번은 관련 명령어만 기입한다.
 - ③번은 명령어의 옵션 또는 옵션과 관련된 인자값을 기입하는데, 명령어를 제외한 나머지 부분을 하나의 괄호로 처리한다. ( 예: -d /home/ihduser)
 - ③번의 경우 ②번 명령어가 틀리면 채점하지 않는다.

 

 

6. 다음은 yum과 rpm을 이용해서 패키지를 설치하고 관리하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. telnet이라는 문자열이 들어있는 패키지를 찾는다.
 # yum (      ①      ) telnet

나. telnet-server라는 패키지를 설치한다.
 # yum (      ②      ) telnet-server

다. telnet-server라는 패키지가 설치한 파일 및 디렉터리 목록을 출력한다.
 # rpm (      ③      ) telnet-server

라. telnet-server라는 패키지를 제거한다.

 # rpm (      ④      ) telnet-server

■ 조건
 - ①번과 ②번은 관련 명령어만 기입한다.

 

 

7. 다음은 시스템 로그 관련 설정을 하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. mail 관련된 모든 메시지는 /var/log/mail에 기록하는데, debug 수준(priority)의 로그는 제외한다.
 (      ①      )   /var/log/maillog

나. uucp 및 news에서 발생하는 warning 수준(priority) 이상의 메시지는 /var/log/news에 기록한다.
 (      ②      )  /var/log/news

다. 모든 서비스(facility)에 대해 alert 수준(priority) 이상의 메시지는 IP 주소가 192.168.12.22인 호스트에 UDP 기반으로 전달한다.
 (      ③      ) (      ④      )

■ 조건
 - ①번 ~ ③번은 facility.priority 형식으로 기입한다.
 - ④번은 관련 설정(action)을 기입한다. 

 

 

8. 다음은 시스템 로그 관련 파일 및 명령어에 대한 설명이다. 설명에 해당하는 파일명이나 명령어를 적으시오. 

가. 시스템에 발생하는 표준 메시지가 기록되는 파일로 대부분의 로그가 이 파일에 쌓인다. 데몬 실행 시 오류가 발생한 경우 이 파일에서 확인하면 유용하다.
 (      ①      )

나. 인증 기반의 접속과 관련된 로그가 기록되는 파일로 telnet이나 ssh 관련 로그는 이 파일에서 확인할 수 있다.
 (      ②      )

다. 사용자의 로그인 정보, 재부팅한 정보 등을 확인할 때 유용한 명령이다.
 (      ③      )

라. 로그인에 실패한 정보를 확인할 때 사용하는 명령이다.
 (      ④      )

■ 조건
 - ①번과 ②번은 관련 파일명을 절대경로로 기입한다.
 - ③번과 ④번은 관련 명령어를 기입한다.

 

 

9. 포트 스캔 도구를 사용해서 로컬 시스템의 열려진 포트를 찾으려고 한다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

- 잘 알려진 포트(Well Known Port)의 열려진 포트를 점검하려고 한다.
 # (      ①      ) (      ②      ) localhost

■ 조건
 - ①번은 관련 명령어만 기입한다.
 - ②번은 ①번 명령어의 옵션 또는 옵션과 관련된 인자값을 기입하는데, 옵션과 관련된 인자값이 있는 경우에는 하나의 괄호로 처리한다. (예: -d /home/ihduser)

 

 

10. 다음은 텍스트 파일을 변환한 후에 파티션 단위로 백업하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. a.txt 파일 안에 있는 문자를 전부 소문자로 변환해서 b.txt 파일로 저장한다.
 # dd (      ①      )

나. /dev/sdb1의 내용을 그대로 /dev/sdc1으로 백업을 진행하는데, 블록 크기는 1KB로 지정한다.
 # dd (      ②      ) bs=1k

■ 조건
 - ①번과 ②번은 명령어 옵션 또는 인자값을 한 번에 기입한다.

 

<작업식 11~16>

11. 개인 사용자의 홈페이지 등록과 관련된 아파치 웹 서버 환경 설정을 진행하려고 한다. ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. 관련 모듈과 환경 설정 파일을 활성화시킨다.
 # vi httpd.conf
 LoadModule (      ①      )
 Include (       ②      )

나. 개인 사용자들의 웹 문서가 위치하는 디렉터리를 www로 지정한다.
 # vi (      ③      )
(      ④      )

■ 조건
 - 아파치 웹 서버는 소스 파일을 이용해서 /usr/local/apache 디렉터리에 설치되어 있다.
 - ①번과 ②번은 관련 설정 내용을 한 번에 기입한다.
 - ③번은 환경 설정 파일명을 절대경로로 기입한다.
 - ④번은 ③번 파일에 존재하는 항목과 값을 하나로 기입하고 대소문자를 구분하여 정확히 기재한다. (예: LogLevel warn)

 

 

12. 다음은 메일 서버를 설정하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. admin 계정으로 들어오는 메일은 관리업무를 담당하는 계정인 ihduser와 kaituser에게 전달되록 설정한다. help계정으로 들어오는 메일은 고객지원센터 부서원의 메일주소가 기록된 /etc/helpdesk 파일에 지정된 사용자들에게 전달되도록 한다.
 # vi (      ①      )
(      ②      )
(      ③      )

나. 변경된 내용이 적용되도록 관련 정보를 갱신시킨다.
 # (      ④      )

■ 조건
 - ①번은 관련 파일명을 절대 경로로 기입한다.
 - ②번과 ③번은 관련 설정을 하나씩 기입한다. (순서 무관)
 - ④번은 관련 명령어 또는 명령어 및 옵션 조합으로 기입한다.

 

 

13. 다음은 NFS 서버를 설정하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

[root@ihd ~]# vi (      ①      )
/ihd    (      ②      )(rw,(      ③      ))
/kait   192.168.12.22(rw,(      ④      ))

■ 조건
 - ①번은 설정 파일명을 절대 경로로 기입한다.
 - ②번 ~ ④번은 관련 설정값을 하나씩 기입한다.
 - /ihd 디렉터리에 접근할 수 있는 호스트는 192.168.5.0 네트워크 대역에 속한 호스트만 허가하고, root 사용자 권한으로 읽기 및 쓰기를 허가한다.
 - /kait 디렉터리에 접근할 수 있는 호스트는 IP 주소가 192.168.12.22인 호스트만 가능하고, root 사용자를 포함해서 모든 사용자 권한은 인정하지 않는다.

 

 

14. firewalld를 이용해서 방화벽 정책을 설정하는 과정이다. 작업 사항에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

 (작업 사항) 웹 서비를 public 영역에 설정하는데, 시스템 재부팅 이후에도 지속적으로 방화벽 정책이 적용되도록 설정한다.

 # (      ①      ) (      ②      ) (      ③      ) (      ④      )

■ 조건
 - ①번은 관련 명령어를 기입한다.
 - ②번부터 ④번은 해당 조건을 만족시킬 수 있는 ①번 명령어의 옵션을 하나씩 기입한다. 순서는 무관하나 ①번 명령어가 틀리면 채점하지 않는다.

 

 

15. 다음은 DNS 서버의 역 존(Reverse zone) 파일을 설정하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

@     IN SOA  (      ①      ) (      ②      ) (
                                  --중략
                                     3H)   ;  minimum
(      ③      )
(      ④      )

■ 조건
 - IP 주소는 10.0.2.15이고, 설정하는 도메인은 ihd.or.kr이다.
 - 관리자 메일 주소는 kait@ihd.or.kr로 설정한다.
 - 네임 서버는 ns.ihd.or.kr을 사용한다.
 - 10.0.2.15인 IP 주소를 조회하면 ihd.or.kr가 나타나도록 설정한다. 

 

 

16. 다음은 텔넷 서버 데몬을 실행하고 TCP Wrapper를 이용해서 접근 제어를 설정하는 과정이다. 조건에 맞게 ( 괄호 ) 안에 알맞은 내용을 적으시오.

가. 텔넷 서버를 실행한다.
 # (      ①      )

나. 모든 서비스에 대해 모든 호스트의 접근을 차단한다.
 # vi (      ②      )
(      ③      )

다. 텔넷 서비스에 대해 192.168.12.0 네트워크 대역에 속한 호스트의 접근을 허가한다.
 # vi (      ④      )
(      ⑤      )

■ 조건
 - ①번은 텔넷 서버를 실행하기 위한 명령을 한 줄로 기입한다.
 - ②번과 ④번은 TCP Wrapper 설정에 사용되는 파일명을 기입한다.
 - ③번과 ⑤번은 제시된 조건에 해당하는 설정값을 한줄로 기입한다.

 

 

좋은 결과 있으시길 바라겠습니다!

 

 다음과 같이 바뀌었다고 합니다. 

버전업으로 인해 그전 기출문제들과는 답이 다른지 어떤지는 전혀 파악을 못했습니다만, 크게 변하진 않은 것으로 보입니다.

 

실제 실기 시험 가보니까 vmware에 CentOS7을 설치해 두고 있었습니다.

마우스도 작동 안 하고 커널만 덩그러니 쓰여있었는데

접속 계정과 암호는 시험 시작하고 칠판에 써서 알려줬습니다.

root=root, root=1234, root=1, centOs=centOs, 등 별의별 계정을 쳐도 로그인이 안되던데..

실제 계정은 root, 암호는 centos 였습니다... 하....ㅋ

 

응시자는 몇몇 분은 30~40대로 보였고 현업 종사자 분들도 응시하러 오신 것 같습니다

 

참고로 올해부터 문제 공개는 하지 않고 문제지는 수험자가 가져갈 수 있어서 

혹시나 자격증 준비하면서 필요로 하시는 분들을 위해 문제 복원하여 업로드하도록 하겠습니다.

 

IT 현업 파이팅

안드로이드 로컬 웹뷰(file:///android_asset/index.html)에서 내부 파일로 페이지 Ajax 이동을 하려고 할 때 (subPage.html) cross origin 에러가 발생한다.

<div data-role="page" id="mainPage">
    <div data-role="header">
        <h1>mainPage Header</h1>
    </div>
    <div data-role="content">
        <h1>mainPage Content</h1>
        <p>
            <a href="subPage.html">subPage로 이동</a>
        </p>
    </div>
    <div data-role="footer">
        <h1>Footer</h1>
    </div>
</div>

 

다음과 같은 웹뷰 세팅으로 해결할 수 있다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        var webView: WebView = findViewById(R.id.web_view)
        
        webView.apply {
      		// Off by default, deprecated for SDK versions >= 30.
            settings.allowFileAccessFromFileURLs = true
            settings.allowUniversalAccessFromFileURLs = true
        }
    }

 

+ api 30부터 deprecated 되었기 때문에 다음과 같이 WebViewAssetLoader를 불러와야한다.

        val assetLoader = WebViewAssetLoader.Builder()
            .addPathHandler("/assets/", AssetsPathHandler(this))
            .addPathHandler("/res/", ResourcesPathHandler(this))
            .build()

        webView.webViewClient = object : WebViewClient() {
            override fun shouldInterceptRequest(
                view: WebView?,
                request: WebResourceRequest
            ): WebResourceResponse? {
                return assetLoader.shouldInterceptRequest(request.url)
            }
        }

<참고>

developer.android.com/reference/androidx/webkit/WebViewAssetLoader

 

WebViewAssetLoader  |  Android 개발자  |  Android Developers

WebViewAssetLoader public final class WebViewAssetLoader extends Object java.lang.Object    ↳ androidx.webkit.WebViewAssetLoader Helper class to load local files including application's static assets and resources using http(s):// URLs inside a WebVie

developer.android.com

pilot376.tistory.com/68stackoverflow.com/questions/63054818/websetting-allowfileaccessfromfileurls-is-deprecated-in-api-lev el-30

 

WebSetting allowFileAccessFromFileURLs is Deprecated in API Level 30

I wanted to use webSetting allowFileAccessFromFileURLs in WebView but the setter is deprecated. how should I set this parameter.

stackoverflow.com

 

4번째 글을 올리는데 많이들 기다리셨을 것이다... (아닐 수두 있고 ㅎ)

대출 심사를 받는 과정은 기다림의 연속이다. 내가 할 수 있는게 별로 없기 때문.

정상적으로 대출 신청이 완료가 되었다면 HUG 혹은 HF 에서 실제 매물을 확인하기 위해 인력을 파견한다. 

그쪽에서 매물을 확인하겠다고 먼저 전화가 오니 모르는 번호도 받아야 한다.

이때 미리 집주인 혹은 부동산에 말해서 며칠에 방문 예정이라고 전달해드리자.

그리고 건물에 대한 보증서가 나오면 은행에서 다시 최종적으로 확인한 뒤에 대출 승인 여부가 나온다.

난 27일에 기금e든든에서 대출승인이 뜨긴 했는데 (승인 문자는 안 옴) 못 보고 있다가 2월 초에 은행에서 전화가 왔다. 부족 서류를 제출하라고.. 그래서 확정일자 받은 계약서를 출력해서 제출했다. (인터넷등기소)

그리고 최종 승인이 됐다. 

난 뭐 승인이 급한건 아니었으니 최종적으로 정확히 3주 걸린 셈인데

행원이 얼마나 일처리를 잘하냐, 얼마나 재촉하느냐에 따라서 2주 정도 걸릴 수도 있을 것 같다.

2월 17일 오전에 대출이 승인되고 2월 19일 사후 자산 심사 최종 완료되었다.

 

 

<타임라인>

14일 오전 : 기금e든든으로 접수

 

15일 오전 : 사전심사 완료

 

18일 오전 : 소득정보가 은행으로 넘어갔다고 해서 아침에 은행 방문. 대출신청. 사후심사 시작.

 

21일 : 주택보증공사에서 인력이 파견되어 실매물 확인

 

27일 : 대출 승인 

 

 

2월 4일 : 확정일자 받은 계약서 제출, 최종 승인

 

2월 19일 사후심사 완료

3. 대출 신청

저번 포스팅 이후로 한참을 쉬었다. 일이 바쁜 것도 있고 코틀린 변환하면서 여러 가지 공부할 것들이 많아져서... 더 미루다간 미완으로 포스팅이 끝날까 봐 연휴 끝난 지금, 마음먹고 작성을 한다.

대출신청이라고 해서 겁먹을 필요는 전~혀 없다. 은행에 필요한 서류를 내면 끝이기 때문에. 

무슨서류가 필요한가 한번 찬찬히 살펴보자. 

모든서류는 대출신청일 기준 1달 이내 발급받아야 한다!!!!

서류는 크게 본인 준비 서류, 부동산 준비 서류, 회사 준비서류 이렇게 3가지로 나누어 볼 수 있다.

회사 준비서류는 회사에 요청해서 받으면 되기 때문에 난이도가 제일 간단하다. 회사 직인은 반드시 있어야 하니 까먹지 말고 직인 찍어달라고 하자.

부동산 서류도 부동산에서 알아서 해준다. 단, 특이사항에 관련한 내용은 절대 잊지말도록!

이렇게 대출 신청 할 때 이미 5%이상 납입한 영수증이 필요하기 때문에 가계약 맺는 것에 의미가 크게 없다는 것이다. 매물 도망가지 않게 하려는 용도 밖에 되지 않는... 

이 필요서류를 챙겨 은행으로 가서 방문하면 되는 아주 심플한 미션.

서류 제출만 하고 행원이 하라는대로 태블릿에 이름과 서명을 쭉쭉쭉 하다보면 끝이 나는 과정이다.

가장 준비를 많이 해야하고 가장 겁먹는 과정이지만 또 가장 별거 없는 과정이 바로 이 대출신청 부분이다.

그리고 한가지 팁을 알려주자면 방문할 은행에 미리 전화를 해 서류를 준비하고 언제 내방할 예정인데 신청이 괜찮냐고 물어보면 각 은행 중기청 대출 전담자한테 연결을 해 줄 것이다. 내 소중한 반차의 시간을 줄여줄 행동들은 무시하지말자ㅜ

 

 

본인 준비서류

본인 준비서류들이 제일 많지만 실제로는 제일 적은 시간이 걸리는 서류들이다. 

왜냐면 인터넷으로 다 발급이 가능하기 때문에,,,,,

어디서 하면 되는지 친절하게 주소와 스크린숏도 첨부해두었으니 바로 발급받아보자!

준비물은 공동 인증서(구 공인인증서)와 IE, 프린터다!

 

 

1) 주민등록등본, 초본

등본이야 워낙 자주들 떼 보셨을 테니.. 그래도 편하게 발급받으시라고 이렇게 주소를 남겨둔다. 

여기를 클릭하면 바로 이동이 가능하다.

 

 

2) 가족관계 증명서

같은 정부 24에서 발급이 가능하다.

여기를 클릭하면 바로 이동이 가능하다.

 

3) 건강보험 자격득실 확인서 

아주 간단하다. 건강보험 메인 페이지에서 아래 클릭만 하면 공인인증서 로그인만 해도 뽑을 수 있다.

여기를 클릭하면 바로 이동이 가능하다.

 

 

4) 건강보험료 납부확인서 

자격득실 확인서 바로 밑에 납부확인서 출력이 있다.

여기를 클릭하면 바로 이동이 가능하다.

 

5) 4대 보험 가입확인서 

여기를 클릭하면 바로 이동이 가능하다.

 

6) 고용보험 피보험 자격 이력 내역서

원래 고용보험 토털 서비스에서 하는 건데 메뉴 찾기 더럽게 힘들게 해 놨다.

 

맘 편하게 정부 24로 가자.

여기를 클릭하면 바로 이동이 가능하다.

 

7) 근로소득원천징수 영수증

My홈택스에 들어가면 지급명세서가 있다.

여기를 클릭하면 바로 이동이 가능하다.

 

 

 

[Kotlin] / [코틀린]

ApplicationContext 가져오기

 

안드로이드에 이 context 라는 것은 매!우! 코딩을 귀찮게 하는 원인이다..

특히 컴포넌트의 color를 변경하거나 resource에 접근할 때도 context를 인자값으로 받는데, 왜 굳이 이런 것에까지 필요한지 모르겠다..

그래서 applicationContext 를 받아와 리소스에 접근하는 것이 효율적으로 판단.

어디서나 접근 할 수 있는 context 를 만들어본다.

 

App.kt

import android.app.Application
import android.content.Context

class App : Application() {

    init{
        instance = this
    }

    companion object {
        var instance: App? = null
        fun context() : Context {
            return instance!!.applicationContext
        }
    }

}

 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!--Permission-->
	...

    <application
        android:name=".App" //Name 추가
        android:allowBackup="true"
        
        ...

실제 사용할 때는 아래와 같이 이용한다.

tvProgressMessage?.text = App.context().getString(R.string.loading)

 

[Kotlin] / [코틀린]

Java에서 Kotlin으로 Conversion 작업하기(2)

- [when 문, RequestBody, okHttp : Interceptor, CustomAdapter : ViewHolder ]

 

안드로이드에서 100번 강조해도 모자를 코틀린의 사용.

그 두 번째 시간.

 

1. if문 대신 when문

코틀린에서 when 문은 switch 문과 비슷하다.

범위를 지정할 수도 있고 조건식을 넣을 수도 있다.

        //In Java
        if (itemName == getString(R.string.customer_search)) {  //Customer - Customer Search
            fragment = SearchCustomerFragment()
            fragmentTag = cFunction.TAG_CUSTOMER_SEARCH
        } else if (itemName == getString(R.string.customer_management)) {  //Customer - Customer Management
            fragment = ManageCustomerFragment()
            fragmentTag = cFunction.TAG_CUSTOMER_MANAGE
        } else if (itemName == getString(R.string.survey)) {  //Customer - Sur
        
        ...
        
        
        //In Kotlin
        when (itemName) {
            //Customer - Customer Search
            getString(R.string.customer_search) -> { fragment = SearchCustomerFragment() ; fragmentTag = cFunction.TAG_CUSTOMER_SEARCH }
            //Customer - Customer Management
            getString(R.string.customer_management) -> { fragment = ManageCustomerFragment() ; fragmentTag = cFunction.TAG_CUSTOMER_MANAGE }
            //Customer - Survey
            getString(R.string.survey) -> { fragment = SurveyFragment() ; fragmentTag = cFunction.TAG_SURVEY }
            
            ....

 

2. RequestBody

Java에서 RetroFit 라이브러리를 이용해 HTTP 통신할 때, Multi-part 타입의 데이터를 담기 위해서 미디어 타입을 설정해줘야 한다.

val APPLICATION_JSON = "application/json; charset=utf-8"

//In Java
RequestBody reqIndividual = RequestBody.create(gson.toJson(individualApplication), MediaType.parse(APPLICATION_JSON));
RequestBody reqGuarantee = RequestBody.create(gson.toJson(guarantee), MediaType.parse(APPLICATION_JSON));
RequestBody reqAreaEvaluation = RequestBody.create(gson.toJson(areaEvaluation), MediaType.parse(APPLICATION_JSON));
RequestBody reqExceptionAprv = RequestBody.create(gson.toJson(exceptionAprv), MediaType.parse(APPLICATION_JSON));
RequestBody reqRelationInfo = RequestBody.create(gson.toJson(relationInfo), MediaType.parse(APPLICATION_JSON));

//In Kotlin
val reqIndividual: RequestBody = gson!!.toJson(individualApplication).toRequestBody(APPLICATION_JSON.toMediaTypeOrNull())
val reqGuarantee: RequestBody = gson!!.toJson(guarantee).toRequestBody(APPLICATION_JSON.toMediaTypeOrNull())
val reqAreaEvaluation: RequestBody = gson!!.toJson(areaEvaluation).toRequestBody(APPLICATION_JSON.toMediaTypeOrNull())
val reqExceptionAprv: RequestBody = gson!!.toJson(exceptionAprv).toRequestBody(APPLICATION_JSON.toMediaTypeOrNull())
val reqRelationInfo: RequestBody = gson!!.toJson(relationInfo).toRequestBody(APPLICATION_JSON.toMediaTypeOrNull())

 

3. okHttp : Interceptor

Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option

jvm 버전을 1.6에서 1.8로 올려야 한다는 말인듯.

file - setting에서 jvm 버전 바꿔주고 캐시 지운 뒤 클린 및 리빌딩을 해보자.

난 이래도 안돼서 build.gradle(app)에 밑에 문구를 추가했다...

    kotlinOptions{
        jvmTarget = "1.8"
    }

 

4. CustomAdapter : ViewHolder

(잘못된 예)

       //In Java
        var convertView = convertView
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.layout_item_popup_staff, parent, false)
            mViewHolder = ViewHolder()
            mViewHolder?.txtSequence = convertView.findViewById(R.id.tvSequence)
            mViewHolder?.txtStaffNo = convertView.findViewById(R.id.tvStaffNo)
            mViewHolder?.txtStaffName = convertView.findViewById(R.id.tvStaffNm)
            mViewHolder?.txtStaffPhoneNo = convertView.findViewById(R.id.tvStaffPhoneNo)
            convertView.tag = mViewHolder
        } else {
            mViewHolder = convertView.tag as ViewHolder
        }

        // Data Set(array_NRC)에서 position에 위치한 데이터 참조 획득
        val customer = customers!![position]

        // View에 Data 세팅
        mViewHolder?.txtSequence?.text = (position + 1).toString()
        mViewHolder?.txtStaffNo?.text = customer.employeeNo
        mViewHolder?.txtStaffName?.text = customer.customerNm
        mViewHolder?.txtStaffPhoneNo?.text = customer.telNo
        return convertView
        
        
        //In Kotlin
                mViewHolder = convertView.tag as ViewHolder

        // Data Set(array_NRC)에서 position에 위치한 데이터 참조 획득
        val customer = customers!![position]

        // View에 Data 세팅
        mViewHolder?.txtSequence?.text = (position + 1).toString()
        mViewHolder?.txtStaffNo?.text = customer.employeeNo
        mViewHolder?.txtStaffName?.text = customer.customerNm
        mViewHolder?.txtStaffPhoneNo?.text = customer.telNo
        return convertView

와 이건 좀 놀랍도록 혁명적이다. 간단히 tag라는 메서드 하나로 이렇게 간결해지다니...

 

라고 생각했으나 위의 코드는 잘못된 것이다.

처음 컨버젼을 할 때 아래와 같이 뜨길래 뭣도 모르고 Simplify 눌러줬는데 그러면 if (convertView == null) 일 때의 코드가 다 날아가 버린다. 

그 이유는 현재 View에 safe null 처리를 안 했기 때문이다. 따라서 정상 코드로 만들려면 아래와 같이 '?'을 붙여줘야 한다.

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

        // ViewHoldr 패턴
        if (convertView == null) {
            view = LayoutInflater.from(context).inflate(R.layout.layout_item_popup_staff, parent, false)
            mViewHolder = ViewHolder()

            mViewHolder?.txtSequence = view.findViewById(R.id.tvSequence)
            mViewHolder?.txtStaffNo = view.findViewById(R.id.tvStaffNo)
            mViewHolder?.txtStaffName = view.findViewById(R.id.tvStaffNm)
            mViewHolder?.txtStaffPhoneNo = view.findViewById(R.id.tvStaffPhoneNo)

            view.tag = mViewHolder
        } else {
            mViewHolder = convertView.tag as ViewHolder
            view = convertView
        }

        // Data Set(array_NRC)에서 position에 위치한 데이터 참조 획득
        val customer = customers!![position]

        // View에 Data 세팅
        mViewHolder?.txtSequence?.text = (position + 1).toString()
        mViewHolder?.txtStaffNo?.text = customer.employeeNo
        mViewHolder?.txtStaffName?.text = customer.customerNm
        mViewHolder?.txtStaffPhoneNo?.text = customer.telNo
        return view
    }

 

 

<참조 문서>

dybz.tistory.com/175

 

[Kotlin 문법] 조건문 (if, when)

# if문 1) if문 사용법 코틀린에서 if문의 문법은 특별하지 않다. 하지만 코틀린에서 if문은 특별한 기능이 있다. 바로 값을 리턴할 수 있다. 2) 값을 리턴하는 if문 코틀린에서는 삼항 연산자를 지원

dybz.tistory.com

thomass.tistory.com/20

 

cannot inline bytecode built with jvm target 1.8 into bytecode that is being built with jvm target 1.6. please specify proper '-

that is being built with jvm target 1.6. please specify proper '-jvm-target' option 에러가 뜬다면!! File-settings안의 kotlin compiler 안의 target JVM version을 바꿔준다 바꿔줘도 에러가 뜬다면 File..

thomass.tistory.com

 

 

Room 라이브러리 사용할 때 생기는 오류다.

코틀린으로 변경하고 난 후 Room database 객체를 싱글톤으로 가져오도록 로직을 추가했는데 그것 때문인지 아니면 지금 코틀린 변환하는 작업 때문에 난 에러인지 몰라서 한참 찾았다.

    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.m3s.skylark/com.m3s.skylark.LoginActivity}: java.lang.RuntimeException: cannot find implementation for com.m3s.skylark.MyAppDatabase. MyAppDatabase_Impl does not exist
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2981)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3042)
        at android.app.ActivityThread.-wrap14(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1639)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6780)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
     Caused by: java.lang.RuntimeException: cannot find implementation for com.m3s.skylark.MyAppDatabase. MyAppDatabase_Impl does not exist
        at androidx.room.Room.getGeneratedImplementation(Room.java:94)
        at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952)
        at com.m3s.skylark.MyAppDatabase$Companion$1.invoke(MyAppDatabase.kt:43)
        at com.m3s.skylark.MyAppDatabase$Companion$1.invoke(MyAppDatabase.kt:34)
        at com.m3s.skylark.SingletonHolder.getInstance(SingletonHolder.kt:20)
        at com.m3s.skylark.LoginActivity.onCreate(LoginActivity.kt:65)
        at android.app.Activity.performCreate(Activity.java:6948)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2924)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3042) 
        at android.app.ActivityThread.-wrap14(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1639) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6780) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386) 

impl 파일 원래 room이 알아서 만들어주는 거자나...

 

build.gradle(APP) 안에다가 plug-in 과 dependency 추가해주자.

apply plugin: 'kotlin-kapt'
android {

	...
    
    dependency{
    
    ...
    
    kapt 'android.arch.persistence.room:compiler:1.1.1'
    
    ...
    
    }
}    
    

[Kotlin] / [코틀린]

Java에서 Kotlin으로 Conversion 작업하기(1)

- [import, String templates, Companion Object, Fragment, ListView Adapter, Lamda, let]

 

안드로이드에서 100번 강조해도 모자를 코틀린의 사용.

그동안 항상 미뤄왔지만 코드의 간결성, 유지보수적인 측면, 개발자로서의 역량강화 등의 이유로 코틀린 변환 작업을 시작해보려고 한다.

오늘부터 코틀린을 시작할 것이기에 모르는 것 투성이고 코틀린의 컴파일 원리도 잘 모르지만 하나하나 시행착오를 겪어가며 글을 계속 수정할 생각이다. 

일단 내가 겪었던 현상 위주로 이 글에 간략히 기록하고 관련 정보를 찾아 업데이트하는 방식으로 진행해보겠다.

 

가장 먼저 안드로이드 스튜디오의 Code메뉴에서 Conevert Java File to Kotlin File을 눌러 컨버전을 실행해보자.

 

 

오른쪽 스크롤의 빠알간 맛을 보자,,,

후.. 이제 하나하나 Java와의 차이점을 기록해보자.

 

1. Import

//In Java
import com.m3s.skylark.model.Employee

//In Kotlin
import com.m3s.skylark.modelimport.Employee

import 하는 것 부터 다르다.. 해당 Employee Class는 model이라는 패키지 안에 저장되어 있는데, 안의 package 경로가 modelimport로 바뀌었다. 분명 같은 패키지안에 있었는데... 폴더명은 model그대로 인데도 말이다. 

Employee Class를 열고 package 부분을 수정하였다.

 

2. String Templates

흠... 그렇게 이뻐졌는지는 모르겠는데

코틀린에서의 문자열 템플릿이 바뀌었다.

String 간의 연결을 + 연산자로 나타내지 않는 것이다. 바로 변수에 '$' 기호를 붙이는 것만으로 해당 변수를 찾아 매핑한다.

단, 띄어쓰기가 없을 경우에 뒤의 문자열까지 인식해 에러를 발생시킬 수 있다. 

그래서 다음과 같이 중괄호를 같이 써주면 좋다.

val MING = "Minggu"
println("제 이름은 $MING라고 합니다.)

println("제 이름은 ${MING}라고 합니다.)

또 중괄호 안에는 if문과 같은 식을 넣을 수도 있다.

println("제 이름은 ${if (MING.length > 2) MING else "Nothing"} 입니다")

//$표시를 넣고 싶다면 백슬래쉬를 추가
println("\$")

 

 

3. Companion Obeject

코틀린에는 정적(static) 변수 혹은 메서드가 없고, 대신 패키지 내에 함수를 선언하여 사용할 수 있다.

바로 그것이 Companion Object 인데, 해당 클래스 가장 밑부분에 추가하는 것이 규칙이다.

const가 상수 선언이다.

 

4. Fragment 호출하기

Java에서 Fragment를 호출할 때와의 차이가 존재한다.

//In Java
        fragmentManager = getSupportFragmentManager();

        if (findViewById(R.id.fragment_container) != null) {
            if (savedInstanceState != null) {
                return;
            }

            fragmentManager.beginTransaction().add(R.id.fragment_container, new DashBoardFragment()).commit();
        }
        
//In Kotlin
        if (findViewById<View?>(R.id.fragment_container) != null) {
            if (savedInstanceState != null) {
                return
            }
            supportFragmentManager.beginTransaction().add(R.id.fragment_container, DashBoardFragment()).commit()
        }

원래 getSupportFragmentManager()를 호출해서 인스턴스 얻어 사용했지만 여기서는 supportFragmentManager와 같이 변수처럼 사용한다는 점이다. Kotlin으로 변경되면서 싱글톤 패턴을 언어 차원에서 지원하게 되었다. object나 companion object를 사용하면 이것이 가능해진다. 다른 예시로 getActivity()도 activity와 같이 사용할 수 있다.

 

5. ListView에 Adapter 연결

Smart cast to 'ListView!' is impossible, because 'mDrawerListView' is a mutable property that could have been changed by this time

변경될 수 있는 var 타입을 다른 변수에 대입하려고 할 때 에러가 발생한다.

따라서 add 하기 전에 변경 불가능한 val 변수에 먼저 값을 세팅한 뒤 val 변수를 add 해주는 방법이 있고

변수 자체에 Non-null 표시인 느낌표 두개를 붙여준다.

Adapter 연결 또한 방식이 다음과 같다.

//In Java
mDrawerListView.setAdapter(customDrawerAdapter);

//In Kotlin
mDrawerListView!!.adapter = customDrawerAdapter

 

6. Lamda 표현식에서 '_' (언더바, 언더스코어)의 사용

람다 식에서 사용하지 않는 변수는 언더바 표시로 바꾼다.

 

7. 확장함수 let

let 함수를 이용해서 객체의 상태를 변경할 수 있다.

itemName이 null이 아니라면 이벤트함수 발생

T?. let { } 형태에서의 let 블록 안에는 non-null 만 들어올 수 있어 non-null 체크 시에 유용하게 쓸 수 있다. 객체를 선언하는 상황일 경우에는 elvis operator(?:)를 사용해서 기본값을 지정해줄 수도 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<참조 문서>

hongku.tistory.com/345

 

코틀린의 문자열 템플릿(Kotlin String Templates)

코틀린에는 문자열 템플릿이란 것이 있습니다. 문자열 안에서 외부에 있는 변수를 가져올 수 있는 방법입니다. 예를들어서 Java에서 "Hello, Kotlin!"을 찍어본다고 합시다. (이때 "Kotlin"은 name이라는

hongku.tistory.com

www.androidhuman.com/2016-07-10-kotlin_companion_object

 

코틀린에는 static이 없다? - companion object

#Android, #Kotlin, and #Tesla

www.androidhuman.com

wdprogrammer.tistory.com/9

 

[Java][Kotlin] Fragment 간단 사용법

2018-10-13-android-fragment 로그인 Fragment를 만들고 메인 액티비티에서 보여주는 것을 한 번 해보자. 추가적으로 java 코드로 보고 코틀린으로 변환해보자. 1. Java 1. Fragment 생성 Fragment는 Android Stud..

wdprogrammer.tistory.com

withhamit.tistory.com/92

 

[kotlin] Smart cast to 'Type' is impossible, because 'xxx' is a mutable property that could have been changed by this time.

data class People(var name: String) // name이 변경될 수 있는 var people: People val names: MutableList  = mutableListOf() people.name?.let {     names.add(people.name) // error..

withhamit.tistory.com

 

blog.yena.io/studynote/2020/04/15/Kotlin-Scope-Functions.html

+ Recent posts