sequel pro를 사용해 MySQL을 쓸 때 사용자 지정 함수를 생성하는 방법은 다음과 같다.

DROP FUNCTION 'ufn_board_no'
delimiter ||
CREATE DEFINER=`test`@`%` FUNCTION `ufn_board_no`() RETURNS int(11)
BEGIN
	DECLARE v_result INT(11);
	SELECT MAX(a.no) + 1 INTO v_result 
	   FROM table_sequense a
	WHERE 1=1
	      AND a.name='board_no';
	if v_result is null then
		set v_result = 1;
	end if;
	INSERT INTO TABLE_SEQUENSE (`no`, `name`) VALUES (v_result, 'board_no');
	RETURN (v_result);
END;
||
delimiter;

 

기존에 있는 함수의 syntax를 확인하면 다음과 같다.

CREATE DEFINER=`test`@`%` FUNCTION `ufn_board_no`() RETURNS int(11)
BEGIN
	DECLARE v_result INT(11);
	SELECT MAX(a.no) + 1 INTO v_result 
	   FROM table_sequense a
	WHERE 1=1
	      AND a.name='board_no';
	if v_result is null then
		set v_result = 1;
	end if;
	INSERT INTO TABLE_SEQUENSE (`no`, `name`) VALUES (v_result, 'board_no');
	RETURN (v_result);
END;

 새로 함수를 생성하려면 Query 탭에서 제일 위의 코드와 같이 다음 코드를 추가해주면 된다.

DROP FUNCTION 'ufn_board_no'
delimiter ||

||
delimiter;
// Grid 출력하기
jQuery(document).ready(function(){
	jQuery("#list1").jqGrid({
		url:'bbsListAjax',
		datatype: 'json',
		mtype: 'POST',
		 jsonReader : { 
				page: "page", 
				total: "total", 
				root: "rows", 
				records: function(obj){return obj.length;},
				repeatitems: false, 
				id: "bbs_no"
			},
		colNames:['수정일자','게시글번호','입력구분','등록일자', '등록자','제목','내용'],
		colModel:[
          	{name:'MODI_DATE',index:'MODI_DATE', width:100, align:"center"},
          	{name:'BBS_NO',index:'BBS_NO', width:80, align:"center"},
          	{name:'BBS_DV_CD',index:'BBS_DV_CD', width:150, align:"center"},
          	{name:'REG_DATE',index:'REG_DATE', width:150, align:"center"},
          	{name:'REG_NO',index:'a.auth_nm', width:150, align:"center"},
          	{name:'SUBJECT',index:'SUBJECT', width:60, align:"center"},
          	{name:'CONTENT',index:'CONTENT', width:150, align:"center"},
        ],
		rowNum:20,
		autowidth: true,
		height:'auto',
		pager: jQuery('#pager1'),
		sortname: 'BBS_NO',
		viewrecords: true,
		sortorder: "desc",
		caption:"게시글목록",
		onSelectRow: function(id){
			if (id)	{
				ajaxEdit(id);
			}
		}
	}).navGrid("#pager1",{search:false,edit:false,add:false,del:false});			

});

데이터를 Json 객체로 받아와 화면에 JqGrid로 출력하려고 하는데 화면에 아무것도 출력되지 않았다.

DB에 아에 샘플 데이터가 없는 경우에는 다음과 같이 문제 없이 파싱이 잘 됐는데 샘플 데이터가 있는 경우에는 파싱이 되지 않는 것을 크롬 개발자 도구를 통해 확인할 수 있었다.

파싱 성공
파싱 실패

{"total":1,"record":1,"page":1,"rows":[{"MODI_DATE":"2020-02-02 02:02:02.0","BBS_NO":"1","BBS_DV_CD":"1","REG_DATE":"2020-06-19 00:16:08.0","SUBJECT":"This is Test","DEL_DATE":"2033-03-03 03:03:03.0","CONTENT":"I have a parsing problem"}]}

Json 객체를 보니 TIMESTAMP에 소수점까지 표시되고 있는걸 확인했다.

SQL SELECT문에서 날짜와 관련된 칼럼명을 모두 지우자 다음과 같이 문제없이 파싱이 되었다.

이를 해결하기 위해 mapper.xml에서 SELECT문에서 BBS.MODI_DATE를 다음과 같이 고치자 소수점이 더 이상 출력되지 않았고 파싱도 문제 없이 잘 되었다.

SELECT
	DATE_FORMAT(BBS.MODI_DATE,'%Y-%m-%d %H:%i:%s') AS MODI_DATE

버튼을 눌러 주사위를 굴려 값을 얻는 예제를 실행하기 위해 다음과 같이 코드를 작성하였다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val rollButton: Button = findViewById(R.id.roll_button)
        rollButton.setOnClickListener{
            rollDice()
        }

        diceImage = findViewById(R.id.dice_image)
    }

    private fun rollDice() {

        val randomInt = Random().nextInt(6) + 1

        val drawableResource = when (randomInt){
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else -> R.drawable.dice_6
        }
        val diceImage: ImageView = findViewById(R.id.dice_image)
        diceImage.setImageResource(drawableResource)
    }
}

26번째 줄을 보면 함수가 실행 될 때마다 diceImage 변수를 생성하여 findViewById 함수를 이용하여 초기화를 하기 때문에 비효율적이다.

val diceImage: ImageView = findViewById(R.id.dice_image)

findViewById 함수는 Id의 트리를 모두 탐색하기 때문에 여러번 호출하는 것은 앱의 실행 시간에 영향을 끼칠 수 있다.

 

따라서 다음과 같이 코드를 수정하였다.

class MainActivity : AppCompatActivity() {

    val diceImage: ImageView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val rollButton: Button = findViewById(R.id.roll_button)
        rollButton.setOnClickListener{
            rollDice()
        }

        diceImage = findViewById(R.id.dice_image)
    }

    private fun rollDice() {

        val randomInt = Random().nextInt(6) + 1

        val drawableResource = when (randomInt){
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else -> R.drawable.dice_6
        }
        val diceImage: ImageView = findViewById(R.id.dice_image)
        diceImage.setImageResource(drawableResource)
    }
}

3번째 줄에 변수로 diceImage를 생성, null로 초기화해준다.

val diceImage: ImageView? = null

그러나 이 코드 역시 변수를 사용하기 위해서 null 체크를 해줘야 하는 번거로움이 남는다.

이를 다음과 같이 lateinit을 사용하면 나중에 변수의 값을 초기화 해줄 수 있다.

lateinit val diceImage: ImageView

하지만 이 코드를 실행하려고 하면 'lateinit' modifier is allowed only on mutable properties 라는 에러가 발생하는데

이는 lateinit 은 immutable(불변) 타입인 val에 사용할 수 없기 때문이다.

따라서 val 변수을 mutable(가변) 타입인 var으로 바꿔주면된다.

lateinit var diceImage: ImageView
class MainActivity : AppCompatActivity() {

    lateinit var diceImage: ImageView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val rollButton: Button = findViewById(R.id.roll_button)
        rollButton.setOnClickListener{
            rollDice()
        }

        diceImage = findViewById(R.id.dice_image)
    }

    private fun rollDice() {

        val randomInt = Random().nextInt(6) + 1

        val drawableResource = when (randomInt){
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else -> R.drawable.dice_6
        }

        diceImage.setImageResource(drawableResource)
    }
}

val 변수의 초기화를 나중에 하고 싶다면 'by lazy'를 사용하면 되는데 val 변수이기 때문에 나중에 한번 초기화한 이후엔 바꿀 수 없다.

val diceImage: ImageView by lazy

 

 

결론

- findViewById 함수를 여러번 호출하는 것은 앱의 실행 시간에 영향을 끼칠 수 있다.

- lateinit val은 불가능 하며 lateinit var를 사용하거나 by lazy를 사용한다.

 

참고
 

Developing Android Apps with Kotlin

Learn the fundamentals of the Kotlin programming language from Kotlin experts at Google.

www.udacity.com

 

response.headers["Content-Disposition"] = "attachment; filename=%s.xlsx" % date

현재의 날짜를 받아와서 날짜를 포함한 파일이름을 저장하려고 하니 다음과 같은 에러가 계속 떴다.

UnicodeEncodeError: 'latin-1' codec can't encode characters

 

dt = datetime.datetime.now()
date = dt.strftime("%m월세금계산서").encode('utf-8')

아무리 변수 타입을 확인해봐도 dt는 str 타입이었고, encode('utf-8')을 했더니 바이트 타입이 되어 그대로 파일명이 'b'12_xec_x9b_x94_xec_x84_xb8_xea_xb8_x88_xea_xb3_x84_xec_x82_xb0_xec_x84_x9c''이 되어버렸다.

 

dt = datetime.datetime.now()
date = dt.strftime("%m월세금계산서").encode('utf-8').decode('iso-8859-1')

encode('utf-8').decode('iso-8859-1') 했더니 문제가 해결되었다.

 

flask @app.route('/')
def index():
    return render_template('index.html')

 

위와 같이 render_template을 사용하였는데 500 internal server error가 뜬다면

index.html 파일이 templates 폴더 안에 있는지 확인

OSError: [Errno 48] Address already in use

 

-> ps -fA | grep python

 

-> kill 36041

 

-> flask run 재실행

+ Recent posts