변수는 보통 단일 자료를 담을 수 있지만 여러 자료를 담아야 할 경우에는 어떻게 해야 할까? 그럴 때는 배열변수를 사용해야 한다. 데이터 타입에 배열을 지정하면 같은 타입의 자료를 모아 하나의 변수에 저장하는 것이 가능한데 이렇게 저장된 배열변수의 값을 꺼내오고 활용하기 위해서는 반복문이 필요하다. 마치 어제 배운 제어문(조건문)의 조건식에  boolean(참과 거짓)값을 넣기 위해 연산자를 활용하는 것처럼.

  배열의 길이를 지정한 경우 배열의 길이를 지정하지 않은 경우
선언 데이터타입[ ] 변수명 = new 데이터타입 [길이]
(ex. int[ ] array = new int[5];)
데이터타입[ ] 변수명 = {값,값,값...}
선언과 초기화를 동시에 진행할 수 있다.
(ex. int[ ] array = {10,20,30};)
초기화 변수명[인덱스(주소)] = 값;
(ex. array[2] = 10;)

 

   배열에 길이를 지정해두고 인덱스값을 참조하여 초기화하거나, 애초 선언과 동시에 중괄호를 활용하여 값을 순서대로 넣을 수 있다. 단, 길이를 지정했을경우 변수로 값이 바뀌지 않는 이상 그 배열에서 더 늘어날 수 없다. 이를테면 int[5]로 5개의 배열을 만들겠다 선언한 뒤 인덱스로 array[5]의 값을 넣게 되어도 출력할 때 인덱스의 범위를 초과했다는 에러창이 뜨게 된다. 자바스크립트에서는 길이를 지정한 뒤 초과해도 큰 문제가 없었지만 자바는 이 점에서 조금 차이가 있는 것 같다.

 

   배열 변수에 담긴 값을 함수에 불러오거나 활용하려면 각 값이 담겨있는 주소, 즉 인덱스를 변수명 뒤에 대괄호를 붙여 호출할 수 있다. 하지만 적은 수의 배열의 값을 꺼내올 때는 [1], [2]... 하나씩 입력하는 것이 쉬울 수 있어도 만약 100개, 1000개, 혹은 그 이상의 값이 들어가 있다면 어떻게 하나하나 호출할 수 있을까.

 

   그래서 오늘은 반복문을 배우게 됐다. 반복문의 종류도 몇 가지가 있다지만 말에서 알 수 있듯 특정 작업을 반복하기 위해 사용하는 명령어다. 이 반복문을 활용하면 배열변수에 있는 값을 꺼내어 활용하는 것은 정말 쉬워진다.

   일단 for문의 기본적인 이용 방법이다. for문은 초기화와 조건식을 통해 참과 거짓을 가려내는 과정을 한번 거치게 된다. 만약 여기서 참이 나오면 중괄호 영역에 쓰인 구문이 실행되고 증감식으로 돌아가 변수의 값을 증가시키거나 혹은 감소시키게 된다. 그러고는 다시 조건식으로 넘어가 증감식을 거친 변수의 값에 따라 참과 거짓을 가려내고 다시 참일 경우 실행, 증감, 조건식 비교, 이 과정을 거짓이 나올 때까지 반복한다. 그래서 반복문이라고 불리는 것이다. 그렇다면 조건식 비교문에 배열이 가진 주소길이 만큼 지정할 수 있다면 활용하는 게 가능하지 않을까?

   개발에서 0은 의미 없는 숫자가 아니다. 0과 1 사이에도 수가 존재하기 때문이다. 그렇기에 배열이 가진 인덱스도 0부터 시작하는데, 이를 활용하기 위해서 늘 초기값을 0으로 주는 습관을 들여야 한다. 그리고 반복할 횟수를 조건식으로 정해야 하는데 직접 3이라고 쓸 수도 있지만 만약 배열의 길이를 모르는 상태라면 .length를 통해 해당 배열변수의 길이를 자동으로 식에 대입할 수 있게 된다. 이렇게만 하면 반복문을 통해 배열변수의 값을 가져오는 것이 가능하다.

(물론 i의 값이 인덱스를 대신하기 때문에 배열변수를 부르는 방식으로 대괄호 안에 i를 넣어주어야 정삭 작동이 된다.)

 

   하지만 반복할 구문이 있다는 것 외에, 이런 배열을 불러오기 위한 for문은 쓰다보면 번거롭지 않을 수가 없다. 그래서 이를 보완하고자 나온 기능이 바로 foreach문이다. foreach문은 기본적으로 for문과 같이 동작하지만 식을 적는 부분에 있어 좀 더 간편화되어 있다.

   자바스크립트에서는 forEach문이라고 따로 썼는데 자바에서는 for문과 똑같이 적어주면 된다. 다만 식이 다를 뿐인데, 먼저 받아 올 데이터의 타입을 적고 임의 변수를 선언한다(꼭 i가 아니어도 된다! for문도 그렇다). 그런다음 콜론을 찍고 배열 변수명을 적어주면 알아서 변수 안에 들어있는 값이 foreach에서 선언한 변수에 담기며 구문을 실행하게 된다. 언제까지? 변수가 가지고 있는 값을 모두 쓸 때까지. 따로 조건식을 쓰지 않아도 알아서 배열변수의 길이만큼 스스로 동작하는 것이다. 이렇게 기능을 돌리면 첫 번째 foreach문에서는 Java Hello Programming 이라는 글자가 공백마다 줄이 바뀐 채 출력되고 두 번째 foreach문에서는 변수에 담긴 모든 값이 더해진 15가 화면에 출력된다.

 

   이런 반복문에는 for문만 있냐면 그건 아니다, while문이 있는데 for문은 반복할 횟수를 정해서 돌리는 것이라면 while문은 정해진 반복횟수가 없다는 것이 특징이다. 물론 while문을 for문처럼 동작하는 것도 가능하지만 일단 기본적인 활용은 그렇다는 것. while문은 조건식에 담긴 값이 true면 무조건 반복하기 때문에 따로 false가 나올 수 있는 환경을 만들어 주어야 한다.

 

   여기서 for문과 while문의 공통점이 보일 것이다, 바로 true와 false값에 따라 작동 여부가 갈린다는 것이다. 이는 if도 마찬가지로 모두 조건식에 비교 연산자를 대입하여 참과 거짓을 받아오거나 boolean값을 직접 입력하는 것으로 문장을 실행시킨다. 그러니 조건식의 자리엔 근본적으로 true와 false를 받는 공간이 되겠다. 위에서 말한대로 while문은 true를 받으면 무한 반복하게 된다, 여기서 빠져나오기 위해 이 true값을 false로 바꿔주는 작업이 필요한데 그것이 여의치 않다면 break를 쓰는 방법이 있다.

break 문 반복문 안에서 사용할 시 반복을 멈추고 해당 코드를 끝낸다.
contiune 문 반복을 일시적으로 멈춘다, 해당 반복문만 멈추고 다시 증감식으로 돌아가 반복을 진행한다.

 

   아직 배운 것이 부족해 더 세세한 사항을 배우지는 못하고 추후에 다시 이 포스터를 확인해야 할 일이 생길 것 같다.

 

   이렇듯 배열에 접근하는 방법을 반복문과 함께 알아 보았는데 단순 늘어놓는 것만 했던 배열변수는 대괄호를 추가하면 다차원의 배열로 만들 수도 있었다. 다차원은 대괄호를 추가한 만큼 2차원, 3차원... 계속 늘어나는 것 같지만 실제로 그렇게 많이 쓰이지는 않고 3차원 이상은 잘 쓰는 경우가 없다고 한다.

 

   그렇다면 2차원 배열의 사용법은 어떻게 될까, 처음에는 긴가민가 했지만 우리가 늘 친숙하게 사용하는 엑셀을 예로 들면 의외로 간단했다. 첫 번째 배열을 엑셀의 열이라 생각하고 두 번째 배열을 행으로 두면 계산하기 편했다. 아래는 그렇게 이미지 메이킹을 하고 작성하여 출력까지 해 본 구문이다.

   배열의 크기를 미리 주는 방법도 있겠지만 일단 초기화를 동시에 해주는 방향으로 작성해보았다. 스스로 4열 3행을 쓴다 생각하고 모든 값을 화면에 출력하는 반복문도 작성했는데 이해한 것이 맞았는지 출력하는 데 문제가 없었다. 일단 기본적으로 배열의 길이는 1차원일 때의 기준인 가로, 즉 열을 기준으로 잡는다. 나는 4열을 기준으로 작성하였으니 길이는 4, 행은 3으로 잡았기 때문에 해당 배열의 길이에서 -1한 값을 두번째 for문의 조건으로 삼았다. 이렇게 쓰는 for문을 중첩 for문이라고도 한다.

   연산자란 특정한 연산을 수행하기 위해 사용하는 기호로 기본적으로 숫자 데이터에서 작동된다. 연산자는 사용 목적에 따라 다양한 종류가 있는데 주로 쓰이는 것은 산술 연산자, 그리고 비교/논리 연산자가 있다. 이와 관련된 것은 자바스크립트를 정리할 때 훑어본 적이 있으니 정리는 넘어가도록 하겠다.

 

   지난 시간에 데이터 타입이 가지고 있는 용량과 사용 목적에 대해 간략히 정리했었는데, 작은 용량에서 큰 용량의 타입으로 넘어가는 것은 문제없이 형변환이 이루어진다고 했다. 이는 연산자를 활용한 변수 대입도 비슷하게 이루어지는데 만약 같은 데이터 타입끼리의 사칙연산의 결과 값을 똑같은 데이터 타입에 담게되는 것은 가능할까? 또, 다른 타입끼리의 사칙연산을 담아낼 데이터 타입은 어떤 것을 써야할까?

데이터 타입 결과 설명
byte + byte = byte
short + short = short
X 작은 범위를 지닌 타입이기 때문에 변수값에 사칙연산이 들어가게 되면 보다 윗 단계 타입에 해당 값을 저장해야 한다 (최소 int 이상)
int + int = int O int부터는 허용할 수 있는 값이 많기 때문에 제약이 없다.
int + long = long O int와 long 중 가장 용량이 큰 데이터 타입을 기준으로 정해주어야 한다. 

 

   위에서 기술하지 않은 연산자 중에 '단항 연산자'가 있다. 단항 연산자는 항이 하나일 때 쓰는 연산자로 기본적으로 단일로 쓰는 경우보다는 조건문이나 반복문 같이 특정한 상황에서만 쓰기 때문에 단독으로 쓰여지는 일은 잘 없지만 이 중에서 우선순위의 개념을 알아볼 수 있었다.

   a의 초기값은 1이다. 출력 이후 후위 증가 연산자를 통해 1의 값을 증가 시켰다. 여기서 증가 연산자가 변수 뒤에 붙었으므로 처리가 맨 마지막에 되어야 하지만 단일로 쓰였기 때문에 실행을 했을 때 눈에 띄는 변화는 없을 것이다. 출력하기 시작하면 얘기가 달라지는데 먼저 ++a로 출력했을 경우, 변수의 값을 먼저 증가시킨 후 출력문이 실행된다. 즉 출력되는 값은 3이 된다. 하지만 a++로 변수 뒤에 증가 연산자를 붙이면 이 또한 출력 값은 3이 된다. 먼저 출력한 이후에 1의 값을 증가시키기 때문이다. 그렇게 마지막에 다시 변수를 출력하면 이후에 증가된 값이 포함되어 4가 나오게 된다.

 

   연산자 중에서도 단연 많이 쓰인다고 할 수 있는 비교 연산자를 알아보자. 비교 연산자는 양변을 비교하여 맞으면 참(True) 틀리면 거짓(False)을 반환해주게 되는데 주로 조건문이나 제어식에서 많이 쓰이니 알아두도록 할 것. 기본적인 사용은 자바스크립트 때 많이 배웠으니 익숙한데 특이한 경우도 있었다.

   int 타입 변수 A에 65라는 정수를 담았고, char 타입 변수 B에는 문자와 함께 연산을 통해 값을 증가시켜주었다. 그렇게 두 변을 비교했더니 변수 A의 값은 B보다 크다 라는 항목에서 아니라는 뜻으로 false가 출력되었다. 문자임에도 알아서 형변환을 통해 두 변의 값을 비교하여 참과 거짓을 가려낸 것이다. B의 항목의 값은 무엇일까, 궁금하여 출력해보면 B에서 1의 값이 증가된 C가 출력되었고, 추가로 B에 산술을 더하자 이 때는 또 68이라는 값이 출력되었다. 알아서 연산자가 들어가면 형변환이 일어나 값을 더하거나 비교할 수 있게 된 것이다. (연산자는 숫자에서만 동작하는 것)

 

   이렇게 두 값을 비교할 수 있는 연산자의 조건을 여러개 할당하고 싶으면 어떻게 해야 할까, 그것을 가능하게 해주는 것이 논리 연산자이다. 논리 연산자는 여러가지 조건을 동시에 검사할 때 사용되며 두 항의 값이 Boolean(true or false)값이 경우에 사용할 수 있다.

   말로 풀어서 설명하면 복잡해 보여도 실제 동작되는 구조를 보면 크게 어렵지 않다. 일단 AND(&&)연산자는 두 항의 조건이 모두 true여야 true를 반환한다. OR(||)연산자는 두 항의 조건 중, 하나의 조건이라도 true가 나오면 true를 반환한다. 이를 곱셈과 덧셈으로 나누어 생각하면 그 원리는 간단하다. 기본적으로 false의 값은 0, true는 1의 값을 가지고 있다. 그렇기 때문에 AND(*)연산자는 하나라도 0이 나오게 되면 이후에 나오는 값에 아무리 1을 곱해주어도 0(false)이 출력될 수 밖에 없다. 반면에 OR(+)는 앞의 조건문에서 계속 0이 나와도 마지막 조건문에서 1이 나온다면 그 조건식은 1(true)가 될 수 있는 것이다.

(물론 순서의 차이도 있다)

 

   그 외에도 기타 연산자에 속하는 삼항 연산자를 살펴보자. 삼항연산자는 항이 세 개인 연산자인데 첫번째 항은 조건식, 그리고 이 조건식이 참과 거짓일 때 각 각 실행해야 하는 구문을 나머지 두 항에 작성하는 연산자를 말한다.

조건문 ? 실행 항목1(조건문이 참일 경우 실행) : 실행 항목2(조건문이 거짓일 경우 실행)

 

   쓰임에 따라 활용할 수 있겠지만 이것은 마치 제어문에서 배우게 될 조건문, if else와 유사한 방식이라 익숙한 느낌이었다. 이 뒤에 배운 대입 연산자 또한 자바스크립트에서의 활용을 마쳤으므로 정리를 생략하도록 하겠다.

 

   이제 위에서도 언급했던 if문을 간략하게 정리하며 후술해보도록 하겠다.

if 문 if(조건식) { 조건식이 참일 경우 실행 }
조건식이 거짓일 경우 무시
true 일 때만 출력
if-else 문 if(조건식) { 조건식이 참일 경우 실행 }
else { 조건식이 거짓일 경우 실행 }
true / false 일 때 둘 중 하나를 출력
if-else if 문 if(조건식1) { 조건식1이 참일 경우 실행 }
else if(조건식2) {
 조건식1이 거짓이며 조건식2는 참일 경우 실행
} else if(...반복){ }
else { 끝내기 }
여러가지의 경우 중
하나를 출력
중첩 if문 if(조건식1){
  if(조건식2){
    조건식1이 참이고 조건식2가 참일 경우 실행
  } else {
    조건식1이 참이나, 조건식2가 거짓일 경우 실행
  }
} else { 조건식1이 거짓일 경우 실행 }
if문의 실행 부분에
다시 if문을 중첩하여 실행 후
조건에 맞는 구문을 출력

 

   기본적으로 자바스크립트와 다른 점이 없어 사용하는데 큰 지장은 없었다.

 

   if문과 비슷하지만 다른 제어문인 switch문도 있다. switch는 if와 다르게 조건문 안에 값이 들어와야 정상적인 사용이 가능하다. 기본적인 흐름이 if-else if와 똑같기 때문에 switch문을 쓰기 보단 if문을 쓰는게 더 편리하다는 게 옥의 티다.

   값의 부분과 일치하는 case를 실행하는 식인데, 여기서 유의할 점은 case가 끝나면 꼭 break를 걸어주어야 한다는 점이다. 위와 같이 break를 쓰지 않게 되면 위의 출력은 case0에서부터 default까지 모든 구문이 출력되게 된다. 꼭 break를 달아주도록 하자. (default : else와 같은 쓰임으로 어느 곳에도 해당하지 않은 값일 경우 출력된다. break문은 쓰지 않아도 된다.)

   JS(자바 스크립트)를 끝내고 Java에 들어갔다. 기초적인 것은 대부분 자바스크립트와 비슷한 양상을 띄는데 한 번 스스로 정리해보는 시간을 가지고 앞으로 다룰 프로그램의 기본적인 생성 방법을 후술하도록 하겠다.


 

   1. 변수                       

   변수란 프로그램 처리 과정에 필요한 데이터를 담아둘 수 있는 저장공간을 말한다. 하나의 값만 저장이 가능하나 언제든 그 값이 변할 수 있다. 변수에 저장된 값은 언제든지 꺼내어 사용이 가능하다. (물론 영역 안에서 선언되어야 한다.)

변경된 값은 덮어쓰기 방식으로 기존의 값이 바뀌는 것이며 기본적으로 누적되는 형식이 아니다.

 

   2. 변수의 선언           

   선언이란 변수를 새로 생성하다, 혹은 정의하다 라는 의미와 똑같다. 말 그대로 변수를 생성해야 데이터를 담을 수 있기 때문에 먼저 선언을 해야한다. 선언을 하는 방법은 데이터를 담을 타입을 정하고 변수명을 입력하면 된다.

 

   3. 변수의 초기화       

   초기화는 선언된 변수에 처음 값을 담을 때를 말한다. 한 번 선언된 변수는 이후에도 값을 바꿀 수 있기 때문에 그런 경우에는 초기화라 하지 않는다. 초기화는 초기 값을 설정할 때만 일컫는 단어이다. 대입연산자(=)를 통해 변수에 값을 담을 수 있다.

 

   4. 변수 이름의 규칙   

   데이터를 담을 변수의 이름은 사용자가 임의로 정할 수 있는데 몇 가지 제약이 있다. 숫자로 시작해서는 안되며, 달러표기($)와 언더바(_)를 제외한 특수문자가 앞에 와서도 안된다. 숫자가 앞에 오면 안되는 이유는 문자열은 따옴표(" ")로 변수와 문자의 구분이 가능하지만 숫자는 별도의 표기가 없기 때문에 혼동될 수 있는 위험이 있기 때문. 그 외에도 변수는 대소문자를 구분하기 때문에 같은 알파벳이라도 대소문자에 따라 전혀 다른 변수가 되니 주의하자.

 

   5. 데이터 타입           

   앞서 변수를 선언하기 위해 데이터 타입을 정해야 한다 했는데, 데이터 타입이란 정수/실수/문자/논리(참과 거짓) 의 종류를 말한다. 이를 담아낼 데이터 타입을 미리 변수 앞에 표시하여 어떤 데이터 유형인지 추측이 가능하게 하는 것이다.

그리고 위의 4개의 타입인 기본 변수 외에도 '참조 변수'가 존재한다. 이는 어떤 객체가 만들어질 때 부여받는 주소를 저장하기 위해 나온 변수로 흔히 문자열을 담을 수 있는 String 또한 참조변수이다.

 

정수형 데이터

byte 1Byte 8 bit -128 ~ 127
short 2Byte 16 bit -32,768 ~ 32,767
int 4Byte 32 bit -2,147,483,648 ~ 2,147,483,647
long 8Byte 64 bit -2⁶³ ~ 2⁶³ -1

 

실수형 데이터

float 4Byte 32 bit 1.4×10⁻  ~ 3.4×10³⁸
double 8Byte 64 bit 4.9×10⁻³²⁴ ~ 1.8×10³⁰⁸

 

문자형 데이터

char 2Byte 16 bit 0 ~ 2¹⁶ -1 (유니코드 ~65535)

 

 

   6. 형변환                    

   생성 때 지정된 데이터 타입을 이후에 바꿀 수도 있는데 이를 형변환이라고 한다. 형변환의 종류에는 자동 형변환과 명시적 형변환이 있는데, 자동 형변환은 일단 기본적으로 변수가 가지고 있는 데이터 값이 변하면 안된다. 그래서 기본적인 제약이 있는데 명시적 형변환은 이를 가능토록 강제로 변환시키는 것을 말한다. 타입을 강제로 바꾸기 때문에 명시적 형변환을 사용하게 되면 변수의 값이 바뀐다는 점이 있다.

 

   데이터 타입이 수용할 수 있는 각 데이터의 크기가 다른데 당연히 데이터 크기가 큰 타입을 보다 작은 타입에 담으려고 하면 에러가 뜬다. 하지만 이럴 때 괄호를 붙여 강제로 데이터 타입을 바꾸면 처리과정을 거쳐 그 데이터 크기에 맞는 값으로 바뀌게 된다. 위와 같이 강제형변환을 한 test_1 변수의 값은 -127로 출력 될 것이다. byte가 담을 수 있는 범위의 한계를 벗어났기 때문에 다시 음수에서부터 시작되어 숫자를 세어가기 때문이다.

   반대로 데이터 타입의 크기가 작은 변수는 보다 큰 타입의 변수에 담는 것이 가능한데, 수용할 수 있는 범위가 넓어 변수에 담긴 값이 변하지 않기 때문이다.

   실수가 담긴 변수를 데이터 타입이 정수인 곳에 담으려 하자 에러가 뜬다. 이는 정수를 표현하는 곳에서 실수(소숫점)를 지원하지 않기 때문인데 이를 강제로 변환하여 출력하게 된다면 소숫점을 없앤 정수만 화면에 출력된다. 하지만 반대로 정수를 실수로 변환하는 것은 가능하다. 숫자 10과 10.0은 같은 수기 때문에 오류없이 잘 작동되는 것을 확인할 수 있다.

   이번에는 문자형에 정수를 담아보았지만 역시 되지 않았다. 하지만 반대로 문자를 정수형에 담아내자 에러 없이 잘 작동되었는데 이는 문자는 애초부터 할당되어 있는 고유의 숫자를 가지고 있기 때문이다.

 


 

 

이번 시간에는 지난번에 만들었던 여행 준비물 체크 리스트에서 '드래그 앤 드롭' 기능을 추가 구현해보았다.

 

 

2024-08-22 :: 022 Java Script 활용 ①

자바 스크립트를 이용하여 나만의 여행 준비물 리스트 목록을 만들어 보았다.생각보다 막히는 부분이 많아서 고생을 좀 했지만...아래는 자바스크립트만 해석하고 기록해보겠다. (CSS는 사용자

hcl-yeon.tistory.com


 

   캡처에는 드래그 부분이 나오지 않지만 준비완료 체크 박스에 아이템이 담긴 것을 확인할 수 있다.

지난시간에 입력하고 추가하는 부분은 작성해두었으니 오늘은 드래그 앤 드롭 부분만 가져와 기록하려고 한다.

 


let dropList = [];
//드롭된 요소들이 담길 배열변수를 선언.
let dropZone = document.querySelector('#drop_box')
//드롭한 아이템들이 담길 div 태그를 들고왔다.

function showList() {
  let list = "<ul id='itemUl' >"
  //기존의 showList 기능으로 만들었던 ul태그를 선택하기 쉽도록 아이디를 붙여주었다.

  for (let i = 0; i < itemList.length; i++) {
    list += `<li>${itemList[i]}<span class='close' id=${i}> X <span></li>`
  }
  list += "</ul>"

  document.querySelector('#itemList').innerHTML = list;

  let itemUl = document.querySelector('#itemUl')
  //변수에 해당 ul태그를 담아두었다.

  //반복문을 사용해 ul태그 안에 생겨날 자식들에게 드래그 효과를 지정해주었다.
  //자식의 인덱스도 0부터 시작하는 것에 유의하자.

  for (let i = 0; i < itemUl.children.length; i++) {
    itemUl.children[i].setAttribute("draggable", "true");
    //드래그가 가능한 상태가 되도록 속성을 추가하는 작업이다.

    itemUl.children[i].addEventListener('dragstart', dragStart)
    //드래그가 시작될 때 이벤트가 발생한다.

    itemUl.children[i].addEventListener('dragend', dragEnd)
    //드래그가 끝날 때 이벤트가 발생한다.
  }

  removeEvent()
}


function dragStart() {
  this.classList.add('dragging');
  //드래그 이벤트가 발생한 요소에 dragging 이라는 클래스를 추가한다.
}

function dragEnd() {
  this.classList.remove('dragging');
  //드래그 이벤트가 끝난 요소에 dragging 이라는 클래스를 삭제한다.
}


// e : 이벤트(event)가 발생한 객체
dropZone.addEventListener('dragover', function (e) {
  e.preventDefault();
  //기본 동작 방지, 폼 제출 방지, 링크 클릭 시 페이지 이동 방지 등... 드래그 앤 드롭을 위한 기본 설정이다.

  dropZone.classList.add('hovered');
  //드래그 한 상태로 해당 div 요소에 오버되면 hovered 라는 클래스를 추가한다.

});

dropZone.addEventListener('dragleave', function () {
  dropZone.classList.remove('hovered');
  //드래그 한 상태로 해당 div 요소를 떠나면 hovered 라는 클래스를 삭제한다.

});

dropZone.addEventListener('drop', function (e) {
  e.preventDefault();
  dropZone.classList.remove('hovered');
  //드래그 한 상태로 해당 div 요소 안에서 드롭되면 hovered 라는 클래스를 삭제한다.

  let item = document.querySelector('.dragging');
  //앞서 드래그 이벤트가 시작되면 추가되는 클래스 속성을 가진 요소를 지역변수에 담는다.


  dropZone.appendChild(item)
  //해당 요소를 div 태그의 자식으로 붙여넣는다.


  let text item.textContent;
  //들고 온 요소의 텍스트만 지역변수에 담는다.

  //span으로 삭제 버튼을 만들어 두었기 때문에 그 요소까지 나타난다.

  let index_str = text.slice(0,text.length-3)
  //span 부분을 삭제하기 위해 3글자를 빼주었다.

  let index = itemList.indexOf(index_str)
  //itemList(input값을 담은 장소)에 담겨있는 글자들과 대조해서 일치하는 값 = 해당 배열의 인덱스가 반환된다.

  if(index != -1){
    itemList.splice(index,1)
    //찾아낸 인덱스 값에서 부터 하나의 요소를 제거한다.

  }

  dropList = this.children
  //드롭이 일어난 요소의 자식들을 해당 배열변수에 담는다. (덮어쓰기)

  for(let i=0; i<dropList.length; i++){
    dropList[i].children[0].id = `drop${i}`
    //배열변수의 첫번째 자식들의 아이디를 초기화한다. (기존 input 태그로 생성한 아이디가 그대로 옮겨오기 때문)

  }

    showList()
  removeEvent()
  //드롭된 이후, itemList들이 가진 id를 재할당하기 위해 기능을 리로드 시킨다.
  //드롭된 부분의 리무브 이벤트를 재할당하기 위해 기능을 리로드 시킨다.


});


function removeEvent() {
  let delBut = document.querySelectorAll('.close')
  for (let i = 0; i < delBut.length; i++) {
    delBut[i].addEventListener("click", delList);
  }

  //기존 리무브 이벤트에서 드롭된 아이템들에 한해 삭제 이벤트를 재설정 해준다.
  for(let i=0; i<dropList.length; i++){
    dropList[i].children[0].removeEventListener("click", delList)
    //먼저 선언되어 있던 기능을 삭제한다.
    dropList[i].children[0].addEventListener("click", delDrop)
    //새로운 삭제 이벤트를 추가한다.
  }
}

function delDrop(){
  dropZone.removeChild(this.parentNode)
  //해당 div의 자식 요소를 삭제하는데, 선택한 요소의 부모를 삭제함으로써 HTML에서 완전히 삭제가 된다.
  //부모 = 드롭장소인 div태그, 자식 = 드롭된 li 태그, 선택한 요소(this) = span 태그
}

   오늘은 랜덤한 숫자값을 맞추는 간단한 게임을 만들어 보았다 :)

 

See the Pen Untitled by IT배움터 (@gfifvcbf-the-typescripter) on CodePen.


     let count = 0;
    //사용자가 몇 번만에 맞췄는지 나타낼 변수다, 초기값은 0

    let alertDiv = document.querySelector('.text_box')
    //사용자가 정답을 맞추거나 틀렸을 경우 문구를 띄울 장소를 가져오기

    let inputNumber = document.querySelector('input')
    //사용자가 입력한 값을 받아오기 위해 input 태그를 들고오기

    inputNumber.id = Math.floor(Math.random() * 100) + 1
    //여기서 랜덤 값을 input의 아이디에 저장하기로 했다. 굳이 id에 줄 필요는 없고 따로 변수를 만들어 그 곳에 저장해도
    똑같은 결과가 나올 것이다... (기능을 좀 더 다양하게 써보기 위해 input태그의 id 공간에 넣기로 했을 뿐)


    let randomId = inputNumber.getAttribute("id")
    //랜덤하게 바뀐 input 태그의 ID를 가져와서 변수에 저장했다.
    //문자열로 저장되기 때문에 숫자로 바꿔줘야 한다.


    let nBtu = document.querySelector('button')
    nBtu.addEventListener('click', numberAnswer)
    //사용자가 버튼을 눌렀을 때 기능이 실행되도록 한다


    function numberAnswer() {
      let userA = Number(inputNumber.value)
      //input태그에 받아온 값을 지역변수로 선언
      //input태그에 담기는 값은 문자열이기 때문에 숫자로 바꿔주는 작업이 필요하다.


      if (userA > 0 && userA < 101) {
        if (userA == randomId) {
          count++
          //사용자가 시도한 횟수 만큼 count 값을 증가시키기 때문에 모든 조건문에 붙는다.
          alertDiv.innerHTML = `정답입니다! ${count}번 만에 맞추셨습니다!`

          inputNumber.id = Math.floor(Math.random() * 100) + 1
          randomId = inputNumber.getAttribute("id")
          //정답을 맞춘 후, 기존 변수를 재선언함으로써 새로운 랜덤값이 생성된다.

          inputNumber.value = ""
            //사용자가 입력한 값을 없앤다.

          count = 0;
          //정답을 맞췄기 때문에 시도횟수는 초기화

        } else if (userA > randomId) {
          count++
          alertDiv.innerHTML = `입력한 값 ${userA}보다 낮은 숫자입니다. 다시 시도해보세요.`
          inputNumber.value = ""

        } else if (userA < randomId) {
          count++
          alertDiv.innerHTML = `입력한 값 ${userA}보다 높은 숫자입니다. 다시 시도해보세요.`
          inputNumber.value = ""
        }

      } else {
        alertDiv.innerHTML = `1~100 사이의 숫자를 입력해주세요.`
        //앞서 사용자가 1~100까지의 숫자를 입력했는지 확인하고 아닐시 else문을 실행한다.
      }
    }

 


   한 글자만 맞아도 검색 목록에 뜨는 화면을 만들어보자.

 

See the Pen Untitled by IT배움터 (@gfifvcbf-the-typescripter) on CodePen.


    let item = [ ];
    //아이템들을 담을 변수 선언

    let itemList = document.querySelectorAll('#itemList > li')
    let itemSearch = document.querySelector('input')
    itemSearch.addEventListener('input', itemValue)
    //input = form 요소의 값이 변경될 때마다 이벤트 발생


    for (let i = 0; i < itemList.length; i++) {
      item.push(itemList[i].innerHTML)
      //배열변수에 li태그가 가지고 있는 텍스트 이름을 모두 저장

    }

    function itemValue() {
      for(let i=0; i<item.length; i++){
        let itemCode = String(item[i]) 
        let userSerach = String(itemSearch.value)

        //includes = 해당 문자열이 포함이 되어 있을 경우 true를 반환

        if(itemCode.includes(userSerach)){
          itemList[i].style.display = "block"
        } else{
          itemList[i].style.display = "none"
        }
      }
    }
    

 


    let itemList = document.querySelectorAll('#itemList > li')
    let itemSearch = document.querySelector('input')

    //keyup = 키보드를 누를 때마다 이벤트 발생
    itemSearch.addEventListener('keyup', function(){
      let itemText = itemSearch.value;
      //지역변수에 input태그에서 받아온 값을 키보드를 누를때마다 저장


      itemList.forEach(function(array){
        let listText = array.textContent;
        //textContent = 요소가 가지고 있는 텍스트를 가져옴


        //indexOf = 해당 문자열을 가지고 있을 경우 해당 위치의 인덱스 반환, 없으면 -1을 반환함
        if(listText.indexOf(itemText) !== -1){
          array.style.display = "block"
        } else {
          array.style.display = "none"
        }
      })
         //indexOf로 반환받은 숫자가 양수면 해당 li태그를 block 처리, 음수면 none처리

    })

 

 

   이 화면에서 JS로 만든 기능은 총 3가지 (배너 슬라이드, 네비게이션 메뉴 hover, 드롭다운 박스)

 

 

   < 배너 슬라이드 >   


let heroL = document.querySelector('#heroLeft')
let heroR = document.querySelector('#heroRight')
console.log(heroL, heroR)
//배너에 배치되어 있는 버튼을 가져온다.

let heroS = document.querySelectorAll('.hero_slider')
//움직일 배너 부분을 가져온다.

let heroCount = 0;
//기능 작동에 필요한 변수를 초기화 한다, 지역변수로 할당하면 안된다. (전역에서 변해야 하는 값이므로)

heroL.addEventListener('click', heroLeft)
heroR.addEventListener('click', heroRight)
//가져온 버튼에 이벤트를 할당한다. 클릭하면 작성한 기능이 실행된다.

function heroLeft(){
  heroCount--;
  //왼쪽 버튼을 누르면 1씩 감소된다.

  //변수의 값이 음수가 되는 경우 배너의 인덱스 마지막 부분으로 이동할 수 있도록 한다.
  //배열의 길이는 1부터 시작하지만 인덱스 번호는 0부터 시작하므로 -1을 해주어야 실제 길이가 맞다.
  if(heroCount<0){
    heroCount = heroS.length-1
  }
  heroL.style.backgroundColor = "#fda417"
  heroL.style.color = "white"
  heroR.style.backgroundColor = "white"
  heroR.style.color = "#07092f"
  //누르면 버튼의 색이 교차로 바뀌는 속성을 지정해주었다.

  heroShow()
  //실제 배너가 움직이는 기능을 포함시켜주어야 화면이 움직인다.
}

function heroRight(){
  heroCount++;
  //오른쪽 버튼을 누르면 1씩 증가한다.

  //변수의 값이 배너의 인덱스 수 보다 많아지게 되면 다시 초기값(0)으로 돌린다.
  if(heroCount>=heroS.length){
    heroCount = 0
  }
  heroR.style.backgroundColor = "#fda417"
  heroR.style.color = "white"
  heroL.style.backgroundColor = "white"
  heroL.style.color = "#07092f"

  heroShow()
}

//실제 배너가 움직이는 효과를 내어 줄 기능이다.
function heroShow(){
  console.log('배너')
  heroS.forEach(function(array){
    array.style.transform = `translateX(-${heroCount*100}%)`
  })
}

 

 

   <네비게이션 메뉴 마우스 over/out 기능 >


let navHover = document.querySelectorAll('#nav_ul > li > a')
//네비게이션 메뉴를 가져오기, CSS 선택자로 작성해도 이상없다.


navHover.forEach(function(array){
  array.addEventListener("mouseover", navColor1)
  array.addEventListener("mouseout", navColor2)
  //이벤트는 같은 항목에 여러번 할당할 수 있다.
})
//각 메뉴들에게 over와 out 이벤트를 지정하고 기능을 연결하기


function navColor1(){
  console.log('오버')
  this.style.color = "#fda417"
  // this는 해당 이벤트가 발생한 요소를 가져오게 되는데, 그렇게 가져온 요소의 속성을 변경할 수 있다.

}

function navColor2(){
  console.log('아웃')
  this.style.color = "white"
}

 

 

   <드롭다운 박스>


let testimonial = document.querySelector('#nav_test')
let testBox = document.querySelector('#nav_test_box')
let testCount = 0;
//드롭다운 박스를 클릭을 할 때마다 나타났다 사라지게 만드는 기능이다
//필요한 요소를 HTML에서 가져오고 전역변수로 필요한 카운트를 선언 및 초기화 해둔다.

testimonial.addEventListener('click', navTestbox)
//클릭 이벤트를 두번 주게되면 나타났다, 사라졌다를 순차적으로 반복하기 때문에 이번 기능에서는 그렇게 하면 안된다.

function navTestbox(){
  testCount++;
  //기본적으로 클릭 할 때마다 변수의 값이 1씩 증가하도록 한다.

  //변수의 값을 2로 나누었을 때의 나머지가 0인 경우는 짝수라는 뜻이므로, 짝수일 때 드롭박스가 보이지 않게 한다.
  //반대로 나누었을 때 나머지가 있다면 홀수라는 뜻이므로, 홀수일 때 드롭박스가 나타나게 만든다.
  if(testCount%2==0){
    testBox.style.opacity = "0"
    testBox.style.top = "50px"
  } else{
    testBox.style.opacity = "1"
    testBox.style.top = "25px"
    //투명도와 위치를 조절하여 나타나게 만든다.
  }
}


   이 화면에서 쓰인 JS기능은 탭의 색상을 바꾸는 한가지

   하단의 슬라이드는 CSS애니메이션 효과를 사용해 자동으로 넘어가게끔 만들었으므로 오랜만에 기록해보겠다.

 


let proTab = document.querySelectorAll('.pro_ul > li > a')
//탭의 버튼들을 가져와준다.

proTab.forEach(function(array){
  array.addEventListener('click', projectTap)
})
//각 탭에 이벤트를 할당한다.

function projectTap(){
  proReset()
  //색상 초기화 기능을 만들어 넣었지만 그냥 해당 기능의 구문을 여기에 작성해도 정상적으로 작동한다.
  //아래에서 색상을 바꾼 탭들이 다음으로 넘어갈 때마다 원래 색으로 돌아오게 하는 기능이다.
  //위에서부터 순차적으로 기능이 실행되기 때문에 초기화를 하고 그 이후 if문에 맞는 탭의 색상이 변하게 된다.

  let proID = this.id.charAt(this.id.length-1)
  //클릭한 탭의 속성에서 ID값의 맨 끝자리를 가져와 배열의 숫자와 비교해 같을 경우 색상을 변경하도록 만들었다.
  //탭의 아이디 뒤에는 0부터 5까지의 숫자를 입력해두었기 때문에 비교가 가능하다.

  for(let i=0; i<proTab.length; i++){
    //1씩 증가하는 i의 값이 가져온 ID의 값과 동일하면 색상이 바뀐다.
    if(proID == i){
      proTab[i].style.backgroundColor = "#fda417"
    }
  }
}

//다음 탭으로 넘어가면 원래의 색상으로 돌아오는 기능
function proReset(){
  proTab.forEach(function(array){
    array.style.backgroundColor = "white"
  })
}

 

 

See the Pen Untitled by IT배움터 (@gfifvcbf-the-typescripter) on CodePen.


.pro_slider{
  width: 100%;
  display: inline-block;
  animation: proSlider 4.5s ease-in-out infinite;
  //keyframes로 만든 애니메이션을 지정하기
  //총 과정을 4.5초 동안 진행되게 만들고 애니메이션의 속도를 지정한 후 반복여부를 결정하면 설정은 끝
}

@keyframes proSlider{
  0% {
    transform: translateX(0%);
    //제자리인 상태
  }

  40% {
    transform: translateX(-100%);
    //-100%만큼 이동한 상태
  }

  100%{
    transform: translateX(-100%);
    //그 상태로 일시정지
  }
}

 

 

이미지 주소로만 변경했던 야매(?)슬라이드를 좀 더 부드러운 동작이 담긴 버전으로 구현해보자.

 


let leftB = document.querySelector('#left')
let rightB = document.querySelector('#right')
leftB.addEventListener("click", leftSlide)
rightB.addEventListener("click", rightSlide)

let sliderLi = document.querySelectorAll('.slider_li')
let count = 0;

function leftSlide(){
  count--;
  //왼쪽 버튼을 누를 때마다 count 변수에 담긴 값에서 -1
  if(count < 0){
    count = (sliderLi.length-1);
    //count가 0보다 작은 숫자가 되면 sliderLi의 마지막 인덱스 번호가 들어가게끔 if문 설정

  }
  show() //아래에 작성한 기능, 화면을 넘겨주는 이벤트  
}

function rightSlide(){
  count++;
  //오른쪽 버튼을 누를 때마다 count 변수에 담긴 값에서 +1

  if(count >= sliderLi.length){
    count = 0;
    //count가 sliderLi 배열의 길이보다 크거나 같은 숫자가 되면 count값을 초기로 되돌려 놓음

  }
  show()
}

function show(){
  sliderLi.forEach(function(array){
    array.style.transform = `translateX(-${count*100}%)`
    //for문 혹은 forEach로 배열의 각 요소에게 원하는 CSS속성을 입력함
  })
}

 


 

   이번에는 슬라이드에 이어 탭 형식도 js로 작성해보자. input태그를 통한 radio 방식으로는 그렇게 어렵지 않았는데 js는 HTML에서 읽어온 뒤, 기능을 만들어 적용해야 한다는 점이 아직은 익숙하지 않은 것 같아 상세하게 남겨보도록 하겠다.

 


<section>

    <ul id="tap_list"> //상단의 탭이 될 부분
      <li class="fashion_tab" id="ftap1">Best Sellers</li>
      <li class="fashion_tab" id="ftap2">New Arrivals</li>
      <li class="fashion_tab" id="ftap3">Hot sales</li>
    </ul>
    <div id="pro_list"> //하단의 컨텐츠가 나타나는 부분
      <div class="pro_box1 pro_box2" id="pro1"> ... </div>
      <div class="pro_box1 pro_box3" id="pro2"> ... </div>
      <div class="pro_box1 pro_box2" id="pro3"> ... </div>
      <div class="pro_box1 pro_box3" id="pro4"> ... </div>
      <div class="pro_box1 pro_box2" id="pro5"> ... </div>
      <div class="pro_box1 pro_box3" id="pro6"> ... </div>
      <div class="pro_box1 pro_box2" id="pro7"> ... </div>
      <div class="pro_box1 pro_box3" id="pro8"> ... </div> //최대한 숫자를 활용하는 편이 편하다.
    </div>

  </section>

 


let tabBtu = document.querySelectorAll('.fashion_tap')
//탭 버튼이 될 li들을 불러와서 변수에 담는다.

consol.log 확인 결과
NodeList(3) [li#ftap1.fashion_tap, li#ftap2.fashion_tap, li#ftap3.fashion_tap]
0: li#ftap1.fashion_tap
1: li#ftap2.fashion_tap.active
2: li#ftap3.fashion_tap


for(let i=0; i<tabBtu.length; i++){
  tabBtu[i].addEventListener('click', tabSelected)
}
//배열의 각 요소에 반복문을 활용하여 각 인덱스에 이벤트를 할당해준다.


function tabSelected(){
  let tabID = 'pro_box' + this.id.charAt(this.id.length-1)
  //ftap(숫자) 부분에서 마지막 숫자 부분만 들고와 테이블의 id와 똑같이 만들기

  console.log 확인 결과 //각 탭을 누를 때마다 반환되는 값이다.
  pro_box1
  pro_box2
  pro_box3


  tabActive(this) //클릭한 값을 가져와서 tap 매개변수에 삽입하여 작동하게 만듦.
  showTable(tabID) //지역변수로 선언된 tabID를 해당 기능의 매개변수에 삽입
}


function tabActive(tab){
  for(let i=0; i<tabBtu.length; i++){
    tabBtu[i].className = "fashion_tab";
    //다시 원래 값으로 돌려놓는 반복문
    //아래에서 active 값을 준 것을 다시 초기화 하기 위해서 필요하다.
    //먼저 선언된 기능이기 때문에 초기화가 먼저 돈 이후 active 추가하는 기능이 작동된다.

  }

  tab.className = "fashion_tab active"
  //active를 추가해 css속성을 주기 위함
  //해당 기능에서는 this로 값을 가져옴

}


function showTable(tabID){
  let proBox = document.querySelectorAll('.pro_box1')
  //탭이 변경 될 때마다 제품들이 나타났다 사라졌다 하기 위해 공통된 클래스 값을 가져와 변수에 저장

  for(let i=0; i<proBox.length; i++){
    proBox[i].style.display = "none"
  }

  let proTable = document.querySelectorAll("."+tabID)
  //단일이 아닌 여러개에 스타일을 줘야 하기 때문에 위와 마찬가지로 반복문을 통해 각 배열에 값을 지정해줘야한다.
  if(proTable){
    proTable.forEach(function(array){
      array.style.display = "block"
    })
   //선택된 클래스만 보이도록 none/block값을 지정하면 끝
  }
}


tabActive(tabBtu[0])
showTable('pro_box1')
//첫 화면에도 탭이 보일 수 있도록 초기값을 지정

 

   위 기능을 코드펜으로 작성하면 이렇게 된다.

 

See the Pen Untitled by IT배움터 (@gfifvcbf-the-typescripter) on CodePen.

 

   어려운 듯 알 듯 말 듯... 아직은 낯설지만 친해질 수 있기를...

+ Recent posts