onDragListener가 아닌 onTouchListener를 이용해서 드래그를 구현할 때 생겼던 문제점이 있어서 기억해두고자 포스팅한다.


배경

안드로이드 프로젝트를 작게 진행하고 있는데 onTouchListener에서 ACTION_UP에 관련된 코드를 작성하였지만 작동하지 않은 사례가 있어서 기억하고자 포스팅한다.


코드 상황

아래와 같은 코드로 처음에 제작을 하였으며 onTouch안에다가 함수가 실행되고 Action Value가 어떤것인지 알 수 있도록 로그 코드를 작성하였으며 각 Action Value에 따라서도 case문을 타고 들어갔음을 알기위해 로그 코드를 작성하였습니다.

imageView_Lv1alarmClockCancel.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                Log.e("onTouchTest", "onTouch Entered");

                int action = motionEvent.getAction();

                Log.e("onTouchTest", "Action Value: " + action);
                switch (action) {

                    case MotionEvent.ACTION_DOWN:
                        Log.e("onTouchTest", "ACTION_DOWN");
                        // ...Dragging View Codes
                        break;

                    case MotionEvent.ACTION_UP:
                        Log.e("onTouchTest", "ACTION_UP");
                        // ...
                        break;

                    default:
                        break;
                }
                return true;

            }

        });


결과는 아래와 같습니다. 화면을 터치하는 순간 ACTION_DOWN(Action Value = 0)이 되면서 case문을 타고 가지만 드래그를 시행하고 모든 작업을 마쳤을 때 화면에서 손을 떼었을 때 ACTION_UP이 아닌 ACTION_CANCEL(Action Value = 3)이 들어오는 것을 확인 할 수 있습니다.

즉, onTouch의 함수에서는 화면을 터치했는지 안했는지만을 확인하며 그에 대한 값들만 ACTION 값으로 받아옴을 확인 할 수 있습니다.

03-09 20:01:31.198 7117-7117/com.unopenlab.iamalarm E/onTouchTest: onTouch Entered
03-09 20:01:31.198 7117-7117/com.unopenlab.iamalarm E/onTouchTest: Action Value: 0
03-09 20:01:31.198 7117-7117/com.unopenlab.iamalarm E/onTouchTest: ACTION_DOWN
03-09 20:01:31.241 7117-7117/com.unopenlab.iamalarm E/onTouchTest: onTouch Entered
03-09 20:01:31.242 7117-7117/com.unopenlab.iamalarm E/onTouchTest: Action Value: 3


참고자료

django.test.Client를 통해서 요청 및 응답에 대한 테스트를 간단하게 짜보자


이전 포스팅

이전에 HttpResponse를 이용해서 간단하게 테스트하기를 통해서 HttpResponse객체를 생성하여 요청과 응답에 대한 테스트를 하는 내용에 대해서 작성하였는데 같은 기능을 이번에는 Django가 제공하는 client객체(Fixture)를 이용해보자


다른점

HttpResponse와 달리 django.test.Client를 이용하면 장고 에뮬레이팅에 의해서 실행되기 때문에 서버를 돌릴 필요가 없게 됩니다. Unit Test와 같은 단위 테스팅에 적합합니다.


사용법

기본적인 GET과 POST요청에 대한 사용법은 아래와 같다.

도메인을 함께 적어주는게 아니라 도메인 다음부터 오는 path을 적어줘야한다.

from django.test import Client

client = Client()

...
#GET
response = client.get('path', {'json': 'json', 'json data': 'json data'})

...
#POST
client.post('path', data={'json': 'json'})

...


GET

url을 명시해주고 그 다음에 Query String을 넘겨서 key-value 쌍으로 넣어주시면 됩니다.

from django.test import Client

client = Client()
#GET
response = client.get('url', {'json': 'json', 'json data': 'json data'})

response에는 응답이 오게 됩니다.


POST

url을 명시해주고 그 다음에 담을 데이터를 key-value 쌍으로 넣어주시면 됩니다.

from django.test import Client

client = Client()
#POST
response = client.post('url', data={'json': 'json'})

이 또한 마찬가지로 response에 응답이 오게 됩니다.


Response

응답에 대한 값이나 처리를 하게되는 경우도 있게 되는데 기본적으로 값을 가져오는 것은 다음과 같습니다. HttpResponse와는 다르게 테스팅 하기위한 몇가지 요소도 포함이 되어있습니다. 참조


  • content는 응답에서 body부분을 bytestring으로 가져오는 객체입니다.
response.content


  • context는 response content에서 렌더링한 template들을 반환합니다. 그 반환된 템플릿에서 특정한 값을 가져오고 싶다면 다음 아래처럼 가져올 수 있습니다.
reponse.context['name']


  • Content-Type이 application/json일 때 .json.loads() 혹은 .json()['name']으로 가져 올 수 있습니다.
response.json.loads()
# or
response.json()['name']


참고자료

Django에서 makemigrations와 migrate의 차이는 무엇일까?


Django 홈페이지

Django Migrations에 보면 간략하게 소개가 되어있다.


차이

장고 공식 홈페이지에 들어가면 아래처럼 간략하게 설명이 나와있다.

Django


  • makemigrations: models.py에서 적용한 변경사항이나 추가된 혹은 삭제된 사항들을 감지하여 파일로 생성
  • migrate: 적용되지 않은 migrations들을(설정값들을) 적용시키는 역할


즉, makimigrations는 장고에서 제공하는 모델의 변경사항들을 감지하고 기록하는 역할을 하며 migrate는 그러한 기록된 파일들과 설정값들을 읽어서 그 변경사항을 db에 저장하는 역할을 한다.

Django에서 테스트를 하기위해 RunPython을 이용해서 Database에 값을 넣어보자


소개

Django에서 fixture사용하기에서 fixture를 이용해 django에서 DB에 자료를 추가하는 방법을 보았는데 이번에는 RunPython을 이용해 보겠습니다.


방법

방법은 비교적 간단하며 아래와 같습니다.

  1. [app_name]/models.py에 모델을 정의합니다.
  2. [app_name]/migrations/에 해당 모델에 대한 py를 추가합니다(하단 참조)
  3. ./manage.py makemigrations./manage.py migrate 실행


모델 작성

필요한 모델을 작성합니다. 예시를 위해서 RunPythonExample이라는 모델을 만들었습니다.

[app_name]/.models.py

from django.db import models


[...]


class RunPythonExample(models.Model):
    test_name = models.TextField()
    test_text = models.TextField()


모델에 대한 .py를 migrations 폴더에 추가

모델을 추가하였으니 해달 모델에 대한 값들을 추가하는 코드를 아래처럼 작성해서 migrations의 하위에 추가합니다. 2개의 tuple을 추가해보겠습니다.

run_python_test.py

# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from django.db import migrations


def forwards_func(apps, schema_editor):

    # (app_name, 해당 앱에서 model class명)
    RunPythonExample = apps.get_model("moim", "RunPythonExample")
    db_alias = schema_editor.connection.alias
    RunPythonExample.objects.using(db_alias).bulk_create([
        RunPythonExample(test_name = "test_name_one", test_text = "test_text_one"),
        RunPythonExample(test_name = "test_name_two", test_text = "test_text_two"),
    ])


def reverse_func(apps, schema_editor):

    RunPythonExample = apps.get_model("moim", "RunPythonExample")
    db_alias = schema_editor.connection.alias
    RunPythonExample.objects.using(db_alias).filter(test_name="test_name_one").delete()
    RunPythonExample.objects.using(db_alias).filter(test_name="test_name_two").delete()


class Migration(migrations.Migration):

    dependencies = [
    ]

    operations = [
        migrations.RunPython(forwards_func, reverse_func),
    ]


명령어 실행 및 확인

명령어를 실행해서 이제 확인을 해봅시다.

python ./manage.py makemigrations
python ./manage.py migrate
python ./manage.py runserver

Git에서 rebase를 통해 변경 내역은 남겨두고 rebase를 통해 commit을 합치는 방법을 알아보자


소개

Git에서 작업을 하다보면 오류를 고치거나 테스트를 하기 위해서 commit을 여러번하거나 불필요한 commit을 하게되는 경우가 있다. 이때 코드는 그대로 남겨두고(변경이력은 그대로) commit을 합치는 방법을 rebase를 통해서 알아보자


방법

특정 python 파일을 계속해서 한줄씩 추가해서 5번 commit을 하고 5번째 commit의 코드 내용은 그대로 유지하면서 commit은 4번째 commit으로 남기는 실습을 진행해 보겠습니다.

  1. rebase_test.py를 만든다
  2. print(“First Commit”)을 추가한다.
  3. “Fisrt Commit”이라는 메시지로 commit한다.
  4. 2~3을 “Fifth Commit”까지 반복한다.


예제 코드

위에 나온대로 하면 아래와 같이 나옵니다.

rebase_test.py

print("Fisrt Commit")
print("Second Commit")
print("Third Commit")
print("Fourth Commit")
print("Fifth Commit")


커밋 내역은 다음처럼 나오면 정상입니다. 추가적으로 나중에 remote에 push하는 과정까지 진행해볼 예정이기에 remote에 추가하였습니다.

Commits


이제 현재 코드를 그대로 “Fifth Commit”의 커밋 메시지를 삭제해보겠습니다. 자 터미널이나 쉘에서 다음과 같은 명령어를 칩니다.

git rebase -i HEAD~2
#HEAD를 포함한 이전 2개까지의 커밋을 보여줍니다.


그러면 아래와 같은 텍스트들이 출력이 됩니다.

Rebase Command Result


이제 커맨드들이 나왔으니 커맨드를 이용해줍시다. 우리는 squash를 통해서 현재의 커밋은 유지하고 이전 커밋으로 녹아드는(내역은 유지하고 메시지는 삭제) 작업을 진행하겠습니다.

아래 내용처럼 picksquash로 변경해주고 저장합니다.

Change to squash


그러면 아래와 같은 화면이 나오는데 여기서 삭제하시고 싶은 Commit을 삭제하시면 됩니다. 우리는 “Fifth Commit”을 삭제하겠습니다. 삭제해주시고 저장하시면 됩니다.

After Save


그러면 아래와 같이 코드는 그대로인데 Commit은 “Fifth Commit”은 사라진 것을 확인 할 수 있습니다.

Git Log Result
Code After Rebase


Remote 저장소에 push 하기

이제 remote에 push를 해보도록 하자

git push origin master


그런데 오류가 생긴다… Updates were rejected because the tip of your current branch is behind라고 현재 브랜치가 remote보다 뒤에 있다고 해서 그렇다고 한다..

그래서 이렇게 rebase를 통해 commit을 합친 경우에는 force push를 해줘야한다.

git push origin master -f


정리

  1. git rebase -i HEAD~2를 명령어 창에 입력
  2. 합치고자 하는 commit에서 pick -> squash로 변경 후 저장
  3. 지우고자 하는 commit 메시지를 지우고 저장
  4. force push로 remote 저장소에 저장


주의사항

force push로 remote에 업로드 하기 때문에 master 혹은 develop 브랜치는 피하는게 좋습니다.