1. MouseEvent.button

마우스 이벤트 객체의 버튼 프로퍼티를 활용하면, 마우스 버튼을 눌렀을 때 일어난 이벤트에 대해서 어떤 버튼을 눌러서 일어난 이벤트인지를 정확하게 알아낼 수 있다.

 

내용
0 마우스 왼쪽 버튼
1 마우스 휠
2 마우스 오른쪽 버튼
3 X1 (일반적으로 브라우저 뒤로 가기 버튼)
4 X2 (일반적으로 브라우저 앞으로 가기 버튼)

 

2. MouseEvent.type

이벤트 타입 설명
mousedown 마우스 버튼을 누르는 순간
mouseup 마우스 버튼을 눌렀다 떼는 순간
click 왼쪽 버튼을 클릭한 순간
dblclick 왼쪽 버튼을 빠르게 두 번 클릭한 순간
contextmenu 오른쪽 버튼을 클릭한 순간
mousemove 마우스를 움직이는 순간
mouseover 마우스 포인터가 요소 위로 올라온 순간
mouseout 마우스 포인터가 요소에서 벗어나는 순간
mouseenter 마우스 포인터가 요소 위로 올라온 순간 (버블링이 일어나지 않음)
mouseleave 마우스 포인터가 요소에서 벗어나는 순간 (버블링이 일어나지 않음)

 

3. MouseEvent.위치값

마우스 이벤트 객체에는 마우스 포인터의 위치와 관련된 다양한 프로퍼티들이 있다. 

프로퍼티 설명
clientX, clientY 마우스 포인터의 브라우저 표시 영역에서의 위치
pageX, pageY 마우스 커서의 문서 영역에서의 위치
offsetX, offsetY 마우스 포인터의 이벤트 발생한 요소에서의 위치
screenX, screenY 마우스 포인터의 모니터 화면 영역에서의 위치

 

4. MouseEvent.relatedTarget

mouseenter, mouseleave, mouseover, mouseout 이벤트에는 relatedTarget이라는 프로퍼티가 존재한다. target 프로퍼티는 이벤트가 발생한 요소를 담고 있다면, relatedTarget 프로퍼티는 이벤트가 발생하기 직전(또는 직후)에 마우스가 위치해 있던 요소를 담고 있다.

 

5. KeyboardEvent.type

이벤트 타입 설명
keydown 키보드의 버튼을 누르는 순간
keypress 키보드의 버튼을 누르는 순간 ('a', '5' 등 출력이 가능한 키에서만 동작하며, Shift, Esc 등의 키에는 반응하지 않음)
keyup 키보드의 버튼을 눌렀다 떼는 순간

 

6. KeyboardEvent.key vs KeyboardEvent.code

키보드 이벤트 객체에서는 key와 code 프로퍼티가 자주 사용되는데 key는 사용자가 누른 키가 가지고 있는 값을 나타내고 code는 누른 키의 물리적인 위치를 나타낸다.

 

7. input태그 다루기

이벤트 타입 설명
focusin 요소에 포커스가 되는 순간
focusout 요소에 포커스가 빠져나가는 순간
focus 요소에 포커스가 되는 순간 (버블링이 일어나지 않음)
blur 요소에 포커스가 빠져나가는 순간 (버블링이 일어나지 않음)
change 입력된 값이 바뀌는 순간
input 값이 입력되는 순간
select 입력 양식의 하나가 선택되는 순간
submit 폼을 전송하는 순간

 

8. 스크롤 이벤트

scroll 이벤트는 보통 window 객체에 이벤트 핸들러를 등록하고 window 객체의 프로퍼티와 함께 자주 활용된다.

특히 scrollY 프로퍼티를 활용하면 스크롤된 특정한 위치를 기준으로 이벤트 핸들러가 동작하게 하거나 혹은

스크롤 방향(위로 스크롤 중인지/아래로 스크롤 중인지)을 기준으로 이벤트 핸들러가 동작하게끔 활용할 수도 있다.

'Study > Javascript' 카테고리의 다른 글

Javascript 개념 II  (0) 2020.11.04
Javacript 개념 I  (0) 2020.11.04

1. 이벤트 핸들러 등록하기

HTML의 속성이나 DOM 프로퍼티를 활용해 이벤트를 등록하는 방법 외에 Element.addEventListener('type', 'handler')를 통해서 이벤트 핸들러를 등록할 수 있다.

 

2. 이벤트 핸들러 삭제하기

addEventListener 메소드를 활용해서 이벤트 핸들러를 등록했다면, Element.removeEventListner('type', 'hnadler')를 통해서 이벤트 핸들러를 삭제할 수 있다.

 

3. 이벤트 객체 (Event Object)

이벤트가 발생하면 이벤트 핸들러의 첫 번째 파라미터에는 자동으로 이벤트 객체가 전달된다. 이벤트 객체는 이벤트 종류마다 가지고 있는 프로퍼티가 다르며, 이벤트에 대한 유용한 정보들을 프로퍼티로 가지고 있다.

 

4. 이벤트 버블링 (Event Bubbling)

이벤트는 전파가 된다. 어떤 요소에서 이벤트가 발생하면 해당 요소에 등록된 이벤트 핸들러가 동작하는 것뿐만 아니라 부모 요소로 이벤트가 계속해서 전파되면서 각 요소에도 등록된 이벤트 핸들러가 있다면 차례로 이벤트 핸들러가 동작하는데, 이때, 자식 요소에서 부모 요소로 이벤트가 전파되는 것을 이벤트 버블링(Event Bubbling)이라고 부른다.

참고로 이벤트 버블링은 이벤트 객체의 stopPropagation() 메소드로 버블링을 막을 수 있다.

 

5. 브라우저의 기본 동작

브라우저에는 각 태그별 혹은 상황별로 기본적으로 약속된 동작들이 있다. 예를 들어 마우스 오른쪽 버튼을 클릭하면 상황에 맞는 메뉴 창이 뜬다거나, input 태그에 커서를 두고 키보드 키를 누르면 해당 값이 입력되는 등이 있다. 그런데 만약 이러한 동작들을 막고 싶다면 이벤트 객체의 preventDefault 메소드를 통해 막을 수가 있다.

* 각 HTML 태그들이 가지고 있는 고유한 역할과 의미를 훼손하게 될 수도 있기 때문에 꼭 필요한 경우에만 주의해서 사용해야 한다

window 객체

window 객체는 브라우저 창을 대변하면서 자바스크립트에서 최상단에 존재하는 객체이며, 자바스크립트 코드 어느 곳에서나 항상 접근할 수 있는 객체이기 때문에 전역 객체(Global Object)라고 부릅니다.

* 어떤 프로퍼티나 메소드를 사용하든 결국 전역 객체 내부의 것이기 때문에 앞에 window.을 생략할 수도 있습니다.

 

DOM

DOM이란 document object model의 약자로, 문서 객체 모델입니다. 즉, 웹 페이지에 나타나는 HTML 문서 전체를 객체로 표현한 것 입니다. 이때 각 객체를 노드(Node)라는 용어로 표현하고, 태그는 요소 노드, 문자는 텍스트 노드로 구분됩니다.

 

DOM 트리

HTML의 계층 구조는 DOM에서도 반영되는데 이러한 계층구조를 나무에 비유해서 DOM 트리라고 부릅니다. 각 노드 간의 관계는 부모, 자식, 형제라는 용어로 표현합니다.

 

DOM 이동 시 활용 가능한 프로퍼티

속성(프로퍼티) 유형 특징
element.children 자식 요소 노드 element의 자식 요소 모음(HTMLCollection)
element.firstElementChild 자식 요소 노드 element의 첫 번째 자식 요소 하나
element.lastElementChild 자식 요소 노드 element의 마지막 자식 요소 하나
element.parentElement 부모 요소 노드 element의 부모 요소 하나
element.previousElementSibling 형제 요소 노드 element의 이전(previous) 혹은 좌측(left)에 있는 요소 하나
element.nextElementSibling 형제 요소 노드 element의 다음(next) 혹은 우측(right)에 있는 요소 하나
node.childNodes 자식 노드 node의 자식 노드 모음(NodeList)
node.firstChild 자식 노드 node의 첫 번째 자식 노드 하나
node.lastChild 자식 노드 node의 마지막 자식 노드 하나
node.parentNode 부모 노드 node의 부모 요소 하나
node.previousSibling 형제 노드 node의 이전(previous) 혹은 좌측(left)에 있는 노드 하나
node.nextSibling 형제 노드 node의 다음(next) 혹은 우측(right)에 있는 노드 하나

 

주요 요소 노드 프로퍼티

속성(프로퍼티) 내용 참고사항
element.innerHTML 요소 노드 내부의 HTML코드를 문자열로 리턴 요소 안의 정보를 확인할 수도 있지만,
내부의 HTML 자체를 수정할 때 자주 활용
element.outerHTML 요소 노드 자체의 전체적인 HTML 코드를 문자열로 리턴 outerHTML은 새로운 값을 할당하면
요소 자체가 교체되어 버리기 때문에 주의가 필요
element.textContent 요소 노드 내부의 내용들 중에서 HTML을 제외하고 텍스트만 리턴 textContent는 텍스트만 다루기 때문에 HTML태그를 쓰더라도 모두 텍스트로 처리

 

요소 노드 다루기

  1. 요소 노드 만들기: document.createElement('태그이름')
  2. 요소 노드 꾸미기: element.textContent, element.innerHTML, ...
  3. 요소 노드 추가 혹은 이동하기: element.prepend, element.append, element.after, element.before
  4. 요소 노드 삭제하기: element.remove();

 

HTML 속성 다루기

대부분의 HTML 속성은 DOM 객체의 프로퍼티로 변환이 됩니다. 하지만, 표준 속성이 아닌 경우에는 프로퍼티로 변환이 이 되지 않습니다. 따라서 아래 메소드를 활용하면 표준이 아닌 HTML 속성들도 다룰 수 있습니다.

  1. 속성에 접근하기: element.getAttribute('속성')
  2. 속성 추가(수정)하기: element.setAttribute('속성', '값')
  3. 속성 제거하기: element.removeAttribute('속성')

 

스타일 다루기

자바스크립트로 태그의 스타일을 다루는 방법에는 크게 두 가지가 있습니다.

  1. style 프로퍼티 활용하기: element.style.styleName = 'value';
  2. class 변경을 통해 간접적으로 스타일 적용하기: element.className, element.classList

 

classList의 유용한 메소드

메소드 내용 참고사항
classList.add 클래스 추가하기 여러 개의 값을 전달하면 여러 클래스 추가 가능
classList.remove 클래스 삭제하기 여러 개의 값을 전달하면 여러 클래스 삭제 가능
classList.toggle 클래스 없으면 추가, 있으면 삭제하기 하나의 값만 적용 가능하고,
두 번째 파라미터로 추가 또는 삭제 기능을 강제할 수 있음

'Study > Javascript' 카테고리의 다른 글

Javascript Event 정리  (0) 2020.11.04
Javacript 개념 I  (0) 2020.11.04

1. 자바스크립트로 태그 선택하기

스크립트 메소드 기능 특징
document.getElementById('id') HTML id속성으로 태그 선택하기 id에 해당하는 태그 하나
document.getElementsByClassName('class') HTML class속성으로 태그 선택하기 class에 해당하는 태그 모음(HTMLCollection)
document.getElementsByTagName('tag') HTML 태그 이름으로 태그 선택하기 tag에 해당하는 태그 모음(HTMLCollection)
document.querySelector('css') css 선택자로 태그 선택하기 css 선택자에 해당하는 태그 중 가장 첫번째 태그 하나
document.querySelectorAll('css') css 선택자로 태그 선택하기 css 선택자에 해당하는 태그 모음(NodeList)

 

2. Event, Event Handling, Event Handler

  • Event : 웹 페이지에서 발생하는 일 ex) 버튼 클릭, 스크롤, 키보드 입력 등

  • Event Handling : 자바스크립트를 통해 이벤트를 다루는 일

  • Event Handler : 이벤트가 발생했을 때 일어나야하는 구체적인 동작들을 표현한 코드. 이벤트 리스너(Event Listener)라고도 부른다.

 

3. 이벤트 핸들러를 등록하는 2가지 방법

3-1. 자바스크립트로 해당 DOM 객체의 onclick 프로퍼티에 등록하기

const btn = document.querySelector('#Button'); 
btn.onclick = function() {
	console.log('Hello World'); 
    };

3-2. HTML 태그의 onclick 속성에 바로 표시하기

<button id="myBtn" onclick="console.log('Hello World')">버튼</button>

'Study > Javascript' 카테고리의 다른 글

Javascript Event 정리  (0) 2020.11.04
Javascript 개념 II  (0) 2020.11.04

앨리어싱 (Aliasing)

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = arr1;

위처럼 arr1배열 통째로 arr2에 저장할 수 있다.

그렇다면 아래의 결과는 어떻게 될까?

arr1[0] = 100; 
System.out.println(arr2[0]);

 

100

이유는 arr1을 arr2에 저장할 때, 두 변수는 같은 주소를 가리키게 된다. 즉, arr1과 arr2는 아예 같은 값이 되게 된다.   

이때, arr2를 arr1의 'Alias(가명)'이라고 할 수 있다.

 

그럼 arr2가 arr1과 같은 주소를 가르키는 것이 아니라 배열을 복사해서 저장하고 싶을 때는 어떻게 해야 할까?

int[] arr1 = {1, 2, 3, 4, 5}; 
int[] arr2 = arr1.clone(); 
arr1[0] = 100; 
System.out.println(arr1[0]); 
System.out.println(arr2[0]); 

 

100 1

위처럼 clone이라는 메소드를 사용하면 된다. clone결과 arr1과 arr2는 서로 다른 배열이 된다. 따라서 arr1[0]을 수정해도 arr2[0]에는 영향을 미치지 않고, 1이 출력된다.

 

for-each

for (int i : Array) { 
   System.out.println(i); 
}

처음에 수행 할 때 i는 Array의 0번 인덱스의 값(원소)을 갖게 되고, 그 다음 들어갈 때는 1번 인덱스의 값(원소)을 갖게 된다. 이런 식으로 배열의 마지막 값(원소)까지 가질 수 있게 된다.

 

문자열을 담는 fruitsArray을 만든 후, 원소를 저장하고, for - each문을 활용하여 원소들을 출력해보겠다.

String[] fruitsArray = new String[5]; 
fruitsArray[0] = "사과"; 
fruitsArray[1] = "딸기"; 
fruitsArray[2] = "수박"; 
fruitsArray[3] = "키위"; 
fruitsArray[4] = "오렌지"; 

for (String fruit : fruitsArray) { 
System.out.println(fruit); 
}  
사과  
딸기  
수박 
키위  
오렌지

 

다중 배열

1 2 3 4
5 6 7 8
9 10 11 12

위와 같은 2차원 배열을 생성하기 위해서는 다중 배열을 선언하면 된다.

 

int[][] multiArray = { 
	{1, 2, 3, 4}, 
	{5, 6, 7, 8}, 
	{9, 10, 11, 12} 
};

 

3 x 4 사이즈의 빈 배열은

int[][] multiArray = new int[3][4];

각 대괄호 사이에 사이즈를 넣어준다. 일반적으로 '행(줄)'을 첫 번째 대괄호에, '열(칸)'을 두 번째 대괄호에 넣는다.

위의 표와 같이 값을 넣기 위해서는

for (int i = 0; i < multiArray.length; i++) {
    for (int j = 0; j < multiArray[i].length; j++) {
        multiArray[i][j] = (i * 4 + 1) + j;
    }
}

여기서 multiArray.length는 전체 자리 수 12가 아닌, 행(줄)의 수인 3입니다.

'Study > Java' 카테고리의 다른 글

Java 개념  (0) 2020.06.09

Java ME/SE/EE and Jakarta EE

Java SE (Java Platform Standard Edition)

가장 기본이 되는 표준 에디션의 자바 플랫폼으로 자바 언어의 핵심 기능을 제공한다. 데스크톱, 서버, 임베디드 시스템을 위한 자바 가상 머신 규격 및 API 집합을 포함한다. JAVA EE, ME는 목적에 따라 SE를 기반으로 기존의 일부를 택하거나 API를 추가하여 구성된다. SE는 가장 일반적으로 사용되는데 JDBC나 기본적인 기능이 모두 포함되어 있기 때문에 Android를 개발할 때 주로 SE를 사용한다. 뿐만아니라 네트워킹, 보안, 데이터베이스 접근, 그래픽스 사용자 인터페이스(GUI) 개발 등을 정의한다.

 

Java EE (Java Platform EnterPrise Edition)

자바를 이용한 서버 측 개발을 위한 플랫폼으로 기존 SE에 웹 애플리케이션 서버에서 동작하는 분산 멀티미디어를 제공하는 자바의 기능을 추가한 서버를 위한 플랫폼. JAVA SE에 서버 측을 위한 기능을 부가하였기 때문에 SE 기능을 모두 포함한다.

 

Java ME (Java Platform Micro Edition)

임베디드를 위한 자바 플랫폼이다. Java ME 또는 J2ME 등으로 불리며, 제한된 자원을 가진 휴대전화, PDA, 셋탑박스 등에서 Java프로그래밍 언어를 지원하기 위해 만들어진 플랫폼이다. Java SE의 부분 집합에 모바일 장치를 위한 특수 클래스 라이브러리가 추가된 플랫폼이다.

 

Jarkarta EE(Java Enterprise Edition)

Java SE를 기반으로 하여 그 위에 탑재된 플랫폼으로, Jarkata EE만의 API를 추가로 import해야 사용할 수 있다. 주로 자바로 서버 측 개발을 하기 위해 필요한 플랫폼으로 자바로 구현되는 웹 프로그래밍에서 가장 많이 사용되는 JSPServlet을 포함하여, 데이터베이스에 연동하는 JDBC 외에도 JNDI, JTA, EJB 등과 같이 기업용 애플리케이션을 개발하는데 필요한 여러 가지 도구들과 라이브러리들이 포함된 플랫폼이다.

 

 

 

JVM with byte code, class loader, and JNI

 

자바에서는

1) 소스코드가 자바 컴파일러(javac)에 의해서 JVM을 위한 코드인 ‘Java byte code’를 생성한다.

2) 해당 Java byte codeJVM(Java Virtual Machine) 안에서 한 줄씩 Interprete 된다.

3) JVM을 가지고 있는 특정한 컴퓨터에서 실행되게 된다.

 

자바 컴파일러에서 소스코드.java가 바이트코드.class로 컴파일되고 JVM에서 바이트코드.class가 인터프리트 되어 실행되니 자바는 인터프리트 기능이 있는 컴파일러 언어라 할 수 있다. JVM이 바이트 코드를 실행하는 가장 기초적인 방법이 인터프리터 방식이며 이 외에 JIT 컴파일 방식, 하드웨어와 소프트웨어를 혼합 구현한 방식이 있다.

 

, JVM(Java Virtual Machine)이란 시스템 메모리를 관리하며 자바 기반 애플리케이션을 위한 실행 환경을 제공한다. 원래 프로그램 메모리는 소프트웨어 개발자가 관리하지만, JVM은 위 그림처럼 가비지컬렉션이 메모리를 관리해준다. 일반적인 상호작용은 힙과 스택의 메모리 사용을 확인하고, 더이상 호출되는 객체가 힙에 없다면 제거한다.

 

바이트코드(ByteCode)JVM이 사용자가 작성한 .java 소스 코드 파일을 운영체제에 실행가능한 명령어 집합 파일로 컴파일하는 과정 중에 필요한 코드로 1차적으로 .java 소스 코드파일을 사용자가 작성하면, JVM이 이를 바이트코드로 컴파일하여 .class 파일로 만든다. 이후 2차적으로 운영체제에 작용하는 기계어로 한번 더 컴파일되어 컴퓨터에 JAVA 프로그램이 실행되는 것이다.

클래스 로더(Class Loader)JVM속으로 클래스(.class)를 로드하고 배치하는 작업을 수행해주는 장치로 런타임시 동적으로 클래스를 로드한다.

 

클래스 로더의 기술적 특징으로는

- Hierarchical

Class LoaderHierarchical 하게 생성이 가능하고, Parent class laoder에서, child class loader를 갖는 것과 같이 Class Loader간의 계층형 구조를 갖게 된다. 최상위 Class Loader"bootstrap" Class Loader가 위치 하게 된다.

 

- Delegate load request

Class loading에는 일정한 규칙이 필요하다. 이는 Class Loader가 계측형의 구조를 가지고 있기 때문에, 어느 시점에 Class 로딩 요청을 받으면 상위 Class LoaderLodingClass가 우선권을 가진다.

 

- Have visibility constraint

Child Class LoaderParent Class LoaderClss를 찾을수 있지만, 그 반대의 경우는 불가능하다.

 

- Cannot unload classes

Class Loader에 의해서 LoadingClass들은 unload 될 수 없다.

 

자바 네이티브 인터페이스(Java Native Interface, JNI)는 자바 가상 머신(JVM)위에서 실행되고 있는 자바코드가 네이티브 응용 프로그램(하드웨어와 운영체제 플랫폼에 종속된 프로그램들) 그리고 C, C++ 그리고 어샘블리 같은 다른 언어들로 작성된 라이브러리들을 호출하거나 반대로 호출되는 것을 가능하게 하는 프로그래밍 프레임워크이다.

 

Open JDK

최근에 자바는 OpenJDK를 중심으로 빠르게 변화하고 있다. 올해부터 OpenJDK는 일 년에 두 번 3월과 9월에 새 버전을 공개해 왔다. 점차 OpenJDK 개발에 참여하는 기관도 늘어나고 있다. OpenJDK 개발에 참여하는 인증 기관(기업)15개에 달한다. 또한 OpenJDK 소스를 기반으로 GraalVM과 같은 독특한 기능을 갖춘 새로운 자바가 만들어지고 있다. 예전에 자기 환경에 맞는 WAS를 선택했던 것처럼, 내 환경에 맞는 Java를 선택하고 사용하는 Java 다양성 시대로 진입하고 있다. 빠른 버전 업그레이드, OpenJDK에 참여하는 조직 확대 및 다양한 Java의 등장과 같은 특징을 통해서 자바는 빠르게 변하는 환경에 신속하게 적응하는 역량을 확보하고 있다.

 

OpenJDK의 긍정적인 효과로는 OpenJDK를 기반으로 하는 다양한 JDK가 만들어지고 있다는 것이다. 최근에 자바에 대한 기능 개선 요구가 많아지고 있는데 새로 등장한 개발 언어의 특징과 기능을 요구하기도 하고, 클라우드 인프라를 위한 기능을 요구하기도 한다. JCP의 협의 체제를 근간으로 하는 자바 개발 프로세스는 업계의 다양한 요구 사항을 신속하게 수용하기 어렵다. OpenJDKTCK로 새로운 JDK를 인증하는 체계를 갖고 있습니다. 이런 체계를 통해서 JDK의 통일성과 다양성을 만들어 가고 있다. 대표적인 예가 GraalVMAzulZulu이다. GraalVM을 사용하면 클라우드 네이티브 애플리케이션에 필요한 다양한 기능을 사용할 수 있다. Java 클래스를 실행 파일로 변환하여 빠르게 실행할 수 있고, java프로그램에서 R 코드를 실행하고 컨텍스트로 변수를 공유할 수 있다. C++ 코드를 자바 코드에서 호출할 수 있다. 또한 GraalVM은 자바로 만든 컴파일러를 제공하여, 자바 프로그램 실행 중에 새로운 코드를 컴파일하여 사용할 수 있다.

 

 

*.jar file

1) 일종의 자바 프로젝트 압축 파일로 ZIP 파일 압축 알고리즘을 기반으로 만들어진다. 따라서 2) 반디집, 알집과 같은 zip 프로그램과 호환이 가능하다. JAR 파일은 3) 웹브라우저에서 빠르게 다운로드할 수 있도록, 자바 애플릿을 위한 클래스, 이미지 및 사운드 파일들을 하나의 파일에 압축하여 담고 있는 파일로 4) 사용자의 요청에 의해 웹페이지의 일부로 들어오는 애플릿에는 여러 개의 파일들이 담겨 있을 수 있는데, 각각은 웹페이지와 함께 다운로드 되어야 한다. 이 때 5) 그 파일들을 하나의 파일에 압축하면 다운로드에 소요되는 시간이 절약된다. 자바로 개발한 여러 클래스 파일들 또는 패키지 파일이 있을 때, 이를 하나로 묶으면 그 6) 클래스들을 참조하기도 편하고, 다운 받기도 쉽다. 7) 자바 어플리케이션 프로그램을 개발 후 하나의 파일로 묶어서 실행하게 해준다.

 

jar명령 사용하기

 

(1) 명령 구문 : jar {ctxu} [vfm0M] [jar-file] [manifest-file] [-C dir] files ...

 

(2) {ctxu}는 맨 앞에 나와야 할 필수 옵션이다.

: c (새로운 archive 파일을 생성한다)

: t list table of contents for archive

: x (다음에 지정한 파일들을 archive 파일로 부터 압축을 푼다)

: u (기존 archive 파일에 들어 있는 파일을 수정한다.)

 

(3) [vfm0M]{ctxu} 다음에 오는 옵션이다.

: v (명령화면창으로 진행사항에 관한 메시지를 출력한다.)

: f (archive될 파일이름을 지정한다.)

: m (정보를 manifest파일로 부터 포함한다.)

: 0 (저장만 하고, zip으로 압축하진 않는다.)

: M (추가되는 엔트리에 대한 manifest 파일을 생성하지 않는다.)

 

 

'Study > Java' 카테고리의 다른 글

Java 개념(Aliasing / for-each / 다중배열)  (0) 2020.06.09

홈화면

Function:

- Recycler View를 활용하여 카페 목록 생성

- 각 카페 목록 클릭 시 적립 화면으로 전환

 

 

public class RecyclerActivity extends AppCompatActivity {

    RecyclerView mRecyclerView;
    MyAdapter myAdapter;

    SharedPreferences preferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler);

        mRecyclerView = findViewById(R.id.recyclerView);

        preferences = this.getSharedPreferences("My_Pref", MODE_PRIVATE);

        getMyList();
    }

    private void getMyList() {
        ArrayList<Model> models = new ArrayList<>();

        Model m = new Model();
        m.setTitle("Ediya");
        m.setDescription("ediya Cafe.");
        m.setImg(R.drawable.ediya);
        models.add(m);

        m = new Model();
        m.setTitle("Starbucks");
        m.setDescription("starbucks Cafe.");
        m.setImg(R.drawable.starbucks);
        models.add(m);

        m = new Model();
        m.setTitle("빽다방");
        m.setDescription("bback Cafe.");
        m.setImg(R.drawable.bbackdabang);
        models.add(m);

        m = new Model();
        m.setTitle("Blue bottle");
        m.setDescription("blue_bottle Cafe.");
        m.setImg(R.drawable.bluebottle);
        models.add(m);

        m = new Model();
        m.setTitle("Angel in us");
        m.setDescription("angel_in_us Cafe.");
        m.setImg(R.drawable.angel_in_us);
        models.add(m);

        m = new Model();
        m.setTitle("TomNToms");
        m.setDescription("Tom N Toms Cafe.");
        m.setImg(R.drawable.tomntoms);
        models.add(m);

        m = new Model();
        m.setTitle("Hollys");
        m.setDescription("Hollys Cafe.");
        m.setImg(R.drawable.hollys);
        models.add(m);

        String mSortSetting = preferences.getString("Sort", "ascending");

        if (mSortSetting.equals("ascending")) {
            Collections.sort(models, Model.By_TITLE_ASCENDING);
        }
        else if (mSortSetting.equals("descending")) {
            Collections.sort(models, Model.By_TITLE_DESCENDING);
        }

        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        myAdapter = new MyAdapter(this, models);
        mRecyclerView.setAdapter(myAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.menu, menu);

        MenuItem item = menu.findItem(R.id.search);

        SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                myAdapter.getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {

                myAdapter.getFilter().filter(newText);
                return false;
            }
        });
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.sorting) {
            sortDialog();
            return true;
        }
        if (id == R.id.logout) {
            Logout();
        }

        return super.onOptionsItemSelected(item);
    }

    private void Logout() {
        Intent intent = new Intent(RecyclerActivity.this, MainActivity.class);
        startActivity(intent);
        finish();
        Toast.makeText(RecyclerActivity.this, "성공적으로 로그아웃 하였습니다.", Toast.LENGTH_SHORT).show();
    }
    private void sortDialog() {

        String[] options = {"Ascending", "Descending"};
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setTitle("Sort by");
        builder.setIcon(R.drawable.ic_action_sort);

        builder.setItems(options, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (which == 0) {
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putString("Sort", "ascending");
                    editor.apply();
                    getMyList();
                }

                if (which == 1) {
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putString("Sort", "descending");
                    editor.apply();
                    getMyList();
                }
            }
        });

        builder.create().show();
    }

}

 

쿠폰 화면

Function:

- Firebase의 데이터베이스에서 값을 읽어와 수만큼 도장 출력(초기 생성시 모두 0)

- 스탬프 적립 클릭 시 Qr코드 생성 및 화면 출력

- 각 카페 별 다른 스탬프의 개수를 가진 쿠폰 출력

 

public class MainpageActivity extends AppCompatActivity {

    private Button createQRBtn;
    private Button scanQRBtn;
    private ImageView coupons[] = new ImageView[10];
    private FirebaseAuth mAuth;
    private FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
    private DatabaseReference mReference = firebaseDatabase.getReference();
    private ChildEventListener mChild;

    public int count;

    //private ListView listView;
    //private ArrayAdapter<String> adapter;
    //List<Object> Array = new ArrayList<Object>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mainpage);

        createQRBtn = (Button) findViewById(R.id.createQR);
        scanQRBtn = (Button) findViewById(R.id.scanQR);
        mAuth = FirebaseAuth.getInstance();
        initDatabase();

        createQRBtn.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                Intent intent = new Intent(MainpageActivity.this, CreateQR.class); // class 바꾸기
                startActivity(intent);
            }
        });


        coupons[0] = (ImageView) findViewById(R.id.coupon1);
        coupons[1] = (ImageView) findViewById(R.id.coupon2);
        coupons[2] = (ImageView) findViewById(R.id.coupon3);
        coupons[3] = (ImageView) findViewById(R.id.coupon4);
        coupons[4] = (ImageView) findViewById(R.id.coupon5);
        coupons[5] = (ImageView) findViewById(R.id.coupon6);
        coupons[6] = (ImageView) findViewById(R.id.coupon7);
        coupons[7] = (ImageView) findViewById(R.id.coupon8);
        coupons[8] = (ImageView) findViewById(R.id.coupon9);
        coupons[9] = (ImageView) findViewById(R.id.coupon10);

        FirebaseUser user = mAuth.getCurrentUser();

        if (user != null) {
            String uid = user.getUid();
            mReference = firebaseDatabase.getReference().child("users").child(uid).child("cafeName").child("Starbucks"); // 변경값을 확인할 child 이름
            mReference.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    //adapter.clear();
                    //FirebaseUser user = mAuth.getCurrentUser();
                    //String uid = user.getUid();
                    for (DataSnapshot messageData : dataSnapshot.getChildren()) {
                        String msg2 = messageData.getValue().toString();
                        count = Integer.parseInt(msg2);
                        if(count >= 10) {
                            count = count % 10;
                            for(int i = 0; i < count; i++)
                            {
                                coupons[i].setImageDrawable(getResources().getDrawable(R.drawable.coupon_normal));
                            }
                        }
                        else {
                            for (int i = 0; i < count; i++) {
                                coupons[i].setImageDrawable(getResources().getDrawable(R.drawable.coffee_stamp));
                            }
                        }// child 내에 있는 데이터만큼 반복합니다.
                    }
                    //adapter.notifyDataSetChanged();

                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            });
        }
    }

    private void initDatabase() {
        mReference = firebaseDatabase.getReference("log");
        mReference.child("log").setValue("check");

        mChild = new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        };
        mReference.addChildEventListener(mChild);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mReference.removeEventListener(mChild);
    }


}

 

* 생성된 데이터베이스에서 Stamp-number의 값에 따라 출력 도장 개수 변환

 

스탬프 적립

Function:

- 사용자의 정보와 현재 스탬프의 개수가 포함된 Qr코드를 생성한다.

- 생성된 Qr코드를 통해 Firebase Database의 올바른 위치에 접근한다.

- Master App을 통해 Qr코드를 스캔할 시 스탬프 개수가 1 증가

- 증가된 스탬프 개수가 Firebase Database에 동기화

 

public class CreateQR extends AppCompatActivity {
    private ImageView iv;
    private ImageView logo;
    //private TextView text;
    private Button bt;
    private String text;
    StringBuffer temp = new StringBuffer();
    Random rnd = new Random();

    FirebaseAuth mAuth;
    FirebaseDatabase database;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_qr);

        StringBuilder textToSend = new StringBuilder(); //qr코드에 담을 데이터
        ActionBar actionBar = getSupportActionBar();
        actionBar.setTitle("스탬프 적립");

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        String cafe_name = bundle.getString("cafe_name");


        byte[] mBytes = bundle.getByteArray("logo");
        Bitmap logo_img = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);


        for (int i = 0; i < 20; i++) {
            int rIndex = rnd.nextInt(3);
            switch (rIndex) {
                case 0:
                    // a-z
                    temp.append((char) ((int) (rnd.nextInt(26)) + 97));
                    break;
                case 1:
                    // A-Z
                    temp.append((char) ((int) (rnd.nextInt(26)) + 65));
                    break;
                case 2:
                    // 0-9
                    temp.append((rnd.nextInt(10)));
                    break;
            }
        } //qr코드 고유 시리얼넘버 지정

        iv = (ImageView)findViewById(R.id.qrcode);
        mAuth = FirebaseAuth.getInstance();
        database = FirebaseDatabase.getInstance();
        bt = (Button) findViewById(R.id.coupon_bt);
        logo = (ImageView) findViewById(R.id.imageView);

        logo.setImageBitmap(logo_img);

        FirebaseUser user = mAuth.getCurrentUser();
        if (user != null) {
            // Name, email address, and profile photo Url
            //String Serial_num = new String(temp); //시리얼넘버
            String uid = user.getUid(); //사용자uid
            textToSend.append(uid + "/" + cafe_name);
            text = textToSend.toString();
            try {
                text = new String(text.getBytes("UTF-8"), "ISO-8859-1");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        try{
            BitMatrix bitMatrix = multiFormatWriter.encode(text, BarcodeFormat.QR_CODE,200,200);
            BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
            Bitmap bitmap = barcodeEncoder.createBitmap(bitMatrix);
            iv.setImageBitmap(bitmap);
        }catch (Exception e){}

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(CreateQR.this, RecyclerActivity.class);
                startActivity(intent);
            }
        });
    }
}

 

Firebase Database와 동기화된 쿠폰 화면

*아래와 같이 Stamp의 number가 5일 경우 쿠폰 화면에 도장이 5개 적립

 

 

각 카페별 도장 개수 확인

* 아래와 같이 Starbucks에 9개, TomNToms에 3개, 빽다방에 5개의 도장이 적립되어 있는지 확인한다.

 

Firebase의 Database와 잘 연동되는걸 확인할 수 있다.


통합 코드는 https://github.com/thdalstn6352/MyCouponWallet 참조

Client App scenario

1. Firebase를 이용하여 회원가입과 로그인화면을 구현한다.

2. 로그인에 성공할 경우 저장된 카페 목록이 나타난다.

3. 각 카페별 다른 스탬프 개수를 가진다.

4. 스탬프 적립을 원할 경우 스탬프 적립 버튼을 통해 Qr코드를 생성한다.

5. Master App의 Qr scan기능을 활용하여 Client의 스탬프를 적립한다.

6. 변경된 스탬프의 개수를 Firebase에 수정한다.

7. Client App화면에 변경된 스탬프의 개수가 출력된다.

 

시작 화면 

Function:

- 3초간 splash 이미지가 실행된 후 메인 화면으로 넘어간다.

 

public class Splashscreen extends Activity implements Animation.AnimationListener {
    private static int SPLASH_TIME_OUT = 3000;
    private ImageView mImgViewLogo;
    private Animation anim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splashscreen);
        mImgViewLogo = findViewById(R.id.imgViewSplashLogo);
        anim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.fade_in);
        anim.setAnimationListener(this);
        mImgViewLogo.startAnimation(anim);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                startActivity(new Intent(Splashscreen.this, MainActivity.class));
                finish();
            }
        }, SPLASH_TIME_OUT);
    }

    @Override
    public void onAnimationStart(Animation animation) {   
    }
    @Override
    public void onAnimationEnd(Animation animation) {
    }
    @Override
    public void onAnimationRepeat(Animation animation) {
    }
}

 

로그인 화면

Function:

- 이메일과 비밀번호 항목에 입력한 값이 Firebase Auth에 저장되어 있는 값과 같을 경우 로그인성공

- 로그인 실패시 오류 메시지 출력

- 계정 만들기 클릭시 회원가입 화면으로 전환

 

public class MainActivity extends AppCompatActivity {

    Button login_button;
    TextView alert_message;
    TextView make_account;
    EditText Edit_email;
    EditText Edit_pw;
    FirebaseAuth mAuth;
    
    @Override
    	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Animation alertMessageAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.alert_message_animation);
        
        mAuth = FirebaseAuth.getInstance();
        Edit_email = (EditText) findViewById(R.id.input_email);
        Edit_pw = (EditText) findViewById(R.id.input_pw);
        login_button = (Button) findViewById(R.id.login_button);
        login_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String email = Edit_email.getText().toString().trim();
                String pw = Edit_pw.getText().toString().trim();

                final String TAG = "Login_Activity";

                if ((email != null && !email.isEmpty()) && (pw != null && !pw.isEmpty())) {
                    mAuth.signInWithEmailAndPassword(email, pw).addOnCompleteListener(MainActivity.this, new OnCompleteListener<AuthResult>() {
                        @Override
                        public void onComplete(@NonNull Task<AuthResult> task) {
                            if (task.isSuccessful()) {
                                FirebaseUser user = mAuth.getCurrentUser();
                                Toast.makeText(MainActivity.this, "로그인에 성공하였습니다.",
                                        Toast.LENGTH_SHORT).show();
                                Intent intent = new Intent(MainActivity.this, RecyclerActivity.class); // class 변경하기
                                startActivity(intent);
                            }
                            else {
                                alert_message = (TextView) findViewById(R.id.alert_message);
                                alert_message.startAnimation(alertMessageAnim);
                            }}});
              }}});

        make_account = (TextView) findViewById(R.id.make_account);
        make_account.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, AccountActivity.class);
                startActivity(intent);
            }});
        }}

 

회원가입 화면

Function:

- 각 항목에 올바른 데이터를 입력 후 회원가입 버튼 클릭시 Firebase Auth에 데이터 저장

- 이메일 형식이 아니거나 두 비밀번호가 다를경우 회원가입 실패 메시지 출력

- 이용약관 클릭시 해당 문서 출력(구현 x)

 

public class AccountActivity extends AppCompatActivity {

    Button sign_button;
    EditText name_input;
    EditText email_input;
    EditText pw_input;
    EditText pw_reinput;
    FirebaseAuth mAuth;
    FirebaseDatabase database;
    ImageView setImage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_account);

        name_input = (EditText) findViewById(R.id.sign_up_name);
        email_input = (EditText) findViewById(R.id.sign_up_email);
        pw_input = (EditText) findViewById(R.id.sign_up_pw);
        pw_reinput = (EditText) findViewById(R.id.pw_rewrite);
        setImage = (ImageView) findViewById(R.id.setImage);
        mAuth = FirebaseAuth.getInstance();
        database =  FirebaseDatabase.getInstance();
        pw_reinput.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if(pw_input.getText().toString().equals(pw_reinput.getText().toString())) {
                    setImage.setImageResource(R.drawable.correct);
                }
                else {
                    setImage.setImageResource(R.drawable.error);
                }
            }
            @Override
            public void afterTextChanged(Editable s) {
            }
        });
        sign_button = (Button) findViewById(R.id.sign_button);
        sign_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final String email = email_input.getText().toString().trim();
                String pw = pw_input.getText().toString().trim();
                final String name = name_input.getText().toString().trim();

                if(!android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()){
                    Toast.makeText(AccountActivity.this,"이메일 형식이 아닙니다",Toast.LENGTH_SHORT).show();
                }

                if ((email != null && !email.isEmpty()) && (pw != null && !pw.isEmpty())) {
                    mAuth.createUserWithEmailAndPassword(email, pw)
                            .addOnCompleteListener(AccountActivity.this, new OnCompleteListener<AuthResult>() {
                                @Override
                                public void onComplete(@NonNull Task<AuthResult> task) {
                                    UserModel userModel = new UserModel();
                                    CafeModel cafeModel = new CafeModel();
                                    if (task.isSuccessful()) {
                                        userModel.userName = name;
                                        userModel.userEmail = email;

                                        String uid = task.getResult().getUser().getUid();
                                        database.getReference().child("users").child(uid).setValue(userModel);
                                        database.getReference().child("users").child(uid).child("cafeName").child("Ediya").child("Stamp").child("number").setValue(0);
                                        database.getReference().child("users").child(uid).child("cafeName").child("Angel in us").child("Stamp").child("number").setValue(0);
                                        database.getReference().child("users").child(uid).child("cafeName").child("Blue bottle").child("Stamp").child("number").setValue(0);
                                        database.getReference().child("users").child(uid).child("cafeName").child("Hollys").child("Stamp").child("number").setValue(0);
                                        database.getReference().child("users").child(uid).child("cafeName").child("Starbucks").child("Stamp").child("number").setValue(0);
                                        database.getReference().child("users").child(uid).child("cafeName").child("TomNToms").child("Stamp").child("number").setValue(0);
                                        database.getReference().child("users").child(uid).child("cafeName").child("빽다방").child("Stamp").child("number").setValue(0);

                                        Toast.makeText(getApplicationContext(), "회원가입이 완료되었습니다.", Toast.LENGTH_SHORT).show();

                                        Intent intent = new Intent(AccountActivity.this, MainActivity.class);
                                        startActivity(intent);
                                    } else {
                                        Toast.makeText(getApplicationContext(), "회원가입이 실패하였습니다.", Toast.LENGTH_SHORT).show();
                                    }}});
                }}});
    }}

 

* 회원가입 완료 후 Firebase의 Authentication 화면

 

 

* Firebase의 Database

+ Recent posts