# TIL: 2025-04-25 다음 주부터는 Java 시작한대 ㅠㅠ

heeezni·2025년 4월 26일
post-thumbnail

1. 이벤트리스너와 익명함수

배운 내용

addEventListener( )

  • 이벤트 핸들러를 태그에 직접 호출 : 디자인과 코드가 섞여버림
    좋은 방법: 동적(프로그래밍적)으로 이벤트 핸들러 연결

  • addEventListener( ): js의 프로그래밍적 방법으로 이벤트를 연결하는 방법

익명함수

  • 익명함수: 재사용성이없는 함수는 함수명 없애기

  • addEventListener() 메서드(함수)에 두번째 매개변수값으로는 함수가 올 수 있는데, 이 함수는 딱 그 이벤트 발생 시에만 호출되는 용도이므로 재사용성이 없음 = 익명함수 처리

addEventListener("load", function(){...});

이때 function()이 익명함수

핵심 포인트

  • addEventListener("load", function(){ ... }) 방식으로 쓰는 건 아주 좋은 습관

  • addEventListener("load", ...)을 쓰는 이유:

  1. HTML이 전부 다 로드된 다음에 실행
    그래서 document.getElementById(...) 같은 코드가 실패하지 않음

  2. 코드가 더 유연하고 깔끔해짐
    <body> 같이 HTML에 직접 쓰는 방식보다,
    자바스크립트 코드와 HTML을 분리해서 관리할 수 있어서 더 깔끔함

  3. 여러 개의 이벤트를 붙일 수 있음
    addEventListener는 같은 이벤트를 여러 개 등록 가능해.
    반면 window.onload = function() {} 방식은 한 번만 등록 가능해서 덮어써짐


갤러리 예제


썸네일에서 선택한 이미지 #content에서 크게 보기, scroll기능 구현

    <style>
        #wrapper{
            width: 700px;
            height: 550px;
            margin: auto;
            background-color: beige;
        }
        #aside{
            width: 120px;
            height: 100%;
            background-color: skyblue;
            float: left;
            text-align: center; /* 내부의 컨텐츠 가운데 수평 정렬 */
            overflow: scroll; /* 현재 영역을 넘쳐흐르는 컨텐츠가 있다면 스크롤 처리 */
        }
        #content{
            width: 580px;
            height: 100%;
            background-color: rgb(199, 228, 240);
            float: left;
            text-align: center;
        }
        #aside img{
            width: 100px; 
            /* 너비만 부여하면, html에서 해당 너비에 대한 비율을 유지하여 높이를 알아서 설정 */
            margin-top: 5px; 
            /* 나의 외부영역에 있는 있는 윗부분에서 5px 떨어지기 */
        }
        #content img{
            width: 100%;
        }


    </style>
    <script>

        function createThumb(){
            for(let i=1; i<=9;i++){

                // 태그로 이미지 하나씩 작성x(정적코드X)
                // 프로그램 실행시(동적으로) 이미지 생성하여 aside에 자식요소로 부착하기
                let img=document.createElement("img"); //<img>작성과 동일 (단 화면에 미부착)
                img.src="../../images/geographic/animal"+i+".jpg"; //<img src="">과 동일
                
                //이미지에 클릭 이벤트 연결 (프로그래밍적 이벤트 연결)
                // <img>과 같은 역할
                img.addEventListener("click", function(){
                        //우측의 content 영역에 지금 누른 썸네일 이미지의 큰 버전이 나오게 하자
                        let content=document.getElementById("content");
                        document.querySelector("#content img").src=this.src; // 길게 표시할 필요없이 나!로 지정
                });
                
                let aside=document.getElementById("aside"); //부모얻기
                aside.appendChild(img); //img 요소를 자식으로 부착
            }
        }
            
            
        // 이벤트 핸들러 구현을 태그에 직접 작성하는 방법도 있지만,
        // js의 프로그래밍적 방법으로 이벤트를 연결하는 방법도 있다
        addEventListener("load", function(){ //앞에 window. 가 생략됐다고 생각하기
            createThumb(); //썸네일 생성하는 함수 만들어봄
        }); 
    </script>
<body>
    <!-- <button id="btn">나 눌러봐</button> -->
    <!-- <button id="btn">나 눌러봐</button> -->
    <!-- 똑같음 - this활용하기 -->
    <div id="wrapper">
        <div id="aside"></div>
        <div id="content"><img></div>
    </div>
</body>

새롭게 알게된 기능

overflow: scroll; :현재 영역을 넘쳐흐르는 컨텐츠가 있다면 스크롤 처리
this : 현재 코드가 실행되고 있는 "자기 자신"을 참조하는 키워드

<script>
	let img=document.createElement("img"); //<img>작성과 동일 (단 화면에 미부착)
	img.src="../../images/geographic/animal1.jpg"; //<img src="">과 동일
                
img.addEventListener("click", function(){

	let content=document.getElementById("content");
	document.querySelector("#content img").src=this.src; // 길게 표시할 필요없이 나!로 지정
});
</script>
<body>
 	<div id="content"><img></div>
</body>

→ #content 영역에 이미 img 태그가 임시로 존재하고, 사용자가 썸네일 이미지를 클릭하면 그 img 태그의 src 속성을 클릭한 썸네일 이미지의 src로 변경하는 방식
(src는 이미지 파일이 어디 있는지 경로를 알려주는 역할)


2. 마우스이벤트 (음식 옮기기 게임)

배운 내용

  • 마우스이벤트: 마우스 움직이는 동안 발생
  • "mousemove" "click"
<script>
        addEventListener("mousemove", function(){ 
            //사용자가 일으킨 모든 이벤트는 event 객체가 알고있다
            //저번 예제에서는 키보드 이벤트도 얻어왔었다 ex) keyCode로 아스키코드 얻어오기
            let x = event.clientX;
            let y = event.clientY;
            console.log(`x좌표=${x}, y좌표=${y}`);
        }); 
    </script>

clientX: 브라우저 화면 기준 X좌표
clientY: 브라우저 화면 기준 Y좌표

핵심 포인트

  • 이벤트를 처리하는 함수는 보통 익명함수로 처리(anonymous)
    (+자바에서는 '람다'로 처리)

음식 옮기기 게임


마우스로 음식을 클릭하면 음식이 따라오고
다시 클릭하면 안 따라오게 구현
클릭 시 테두리 주기
script영역에서 접시 만들어보기
#aside에는 마우스 좌표 누적기록

    <style>
        body{
            margin: 0px;
            /* 좌표 계산 쉽게 하려고 */
        }
        #wrapper{
            width: 850px;
            height: 550px;
            background-color: beige;
            border: 5px solid black;
        }
        #content{
            width: 700px;
            height: 100%;
            float: left;
            background-color: beige;
            position: relative;
        }
        #aside{
            width: 150px;
            height: 100%;
            float: left;
            background-color: orange;
            position: relative;
            overflow: scroll; /* 스크롤 주기 */
        }
    </style>
    <script>
        // 프로그램에서 자주 사용될 가능성이 있는 요소들을 초기화 작업 시, 전역변수화 시켜놓자
        let content;
        let aside;
        let img; //다른 함수영역(scope)에서도 접근할 수 있도록 전역으로 빼놓자
        let flag=false; //처음엔 안 따라다니게

        function createFood(){
            img=document.createElement("img"); //<img>
            img.src="../../images/food/hamburger.png"; //<img src="">
            img.style.width=150+"px";
            img.style.position="absolute";
            img.style.left=100+"px";
            img.style.top=180+"px";

            //음식이 따라다니고, 안따라다니고의 기준은 flag 변수이므로
            //유저를 배려하여, 클릭 시마다 논리값을 뒤집어 버리자
            img.addEventListener("click", function(){
                flag=!flag;

                if(flag){
                    this.style.border= "5px solid red"; //테두리는 flag true일때만 적용하자
                    this.style.borderRadius="25%";
                } else {
                    this.style.border="none"; //마우스 클릭 안했을 때는 테두리x
                } 
            });

            content.appendChild(img);
        }

        function createDish(n,x,y){ //접시 만들기
            let div=document.createElement("div");
            div.style.width=n*10+"px";
            div.style.height=n*10+"px";
            div.style.backgroundColor="white";
            div.style.borderRadius=50+"%";
            div.style.position="absolute";
            div.style.left=x+"px";
            div.style.top=y+"px";
            div.style.border = "5px solid black"; // 테두리에는 solid도 반드시 추가

            content.appendChild(div);
        }
 
        function init(){ // 프로그래밍 가동되면, 초기화할 작업이 있을 경우 초기화 함수를 정의하는게 좋은 방법
            
            //content요소에 대해 마우스 움직임 이벤트 연결하기
            content=document.getElementById("content");
            aside=document.getElementById("aside");
            createDish(20,400,150); //접시 등장 시키기 ★함수 만들었으면 호출 필수★
            createDish(10,450,200);
            createFood(); //음식 등장 시키기

            //문서 전체를 대상으로 마우스 이벤트를 연결하자
            document.body.addEventListener("mousemove",function(){ //body는 1개, 굳이 아이디 주지 말자
                console.log("지금 움직임?");
                //마우스에 의한 x,y좌표를 **음식의 좌표와 일치시키면** 따라다니는 효과 가능
                let x=event.clientX;
                let y=event.clientY;

                if(flag){ //아래의 조건식은 flag에 따라 수행될 지 말 지 결정된다.
                    if(x<=(750-110) && y<=(550-70)){ //img 박스 밖으로 안 튀어나가게 하려면?
                    img.style.left= (x-75)+"px" //(변수-이미지의 절반=마우스가 이미지 중심)
                    img.style.top= (y-75)+"px"
                    }
                } 
                //aside 영역에 좌표를 출력하자
                aside.innerHTML=aside.innerHTML+`x=${x}, y=${y}<br>`;
            });
            
        };

        //문서가 로드된 이후에 작업 진행
        addEventListener("load", function(){
            init();
        });
    </script>
<body>
  <div id="wrapper">
      <div id="content"></div>
      <div id="aside"></div>
  </div>
</body>

내가 놓쳤던 부분

  • 함수를 정의했으면 호출해야지... (호출 순서대로 아래에 깔림)
  • body는 1개, 굳이 아이디 주지 않기
document.body.addEventListener("mousemove",function(){ });
  • 변수 - 이미지 크기의 절반 = 마우스가 이미지 중심에 오게 만듦
  • 이분법적 조건일 때는 변수 flag활용
let flag=false;
flag=!flag;
  • 동적으로 <img> 생성
img=document.createElement("img"); // html에서 <img>
img.src="../../images/food/hamburger.png"; // html에서 <img src="">
  • 테두리에는 solid도 반드시 추가
border: 5px solid black;

3. 자동호출 (자동증가 카운트)

배운 내용

  • setInterval(함수,호출시간간격) - 지정한 함수를 지정된 간격으로 무한호출
  • setTimeout(함수. 호출시간간격) - 지정한 함수를 지정된 시간이 흐른 후 호출 (1회 호출)

재귀호출을 이용한 가벼운 무한루프 (setTimeout)

    function test(){
        console.log("나 불렀어?");
        setTimeout(test,100); // 1/1000초 표현
        // 콘솔에서 test(); 호출 시 - 가벼운 무한루프 (재귀호출이용)
    }

setTimeout( 이 자리에 , 100);

"test()" ❌ ← 문자열로 전달 (eval 방식, 비권장)

test() ❌ ← 즉시 실행돼버림 (실행 결과만 전달됨)

test ✅ ← 함수 참조 전달 (정확하고 안전함)

자동증가 카운트

카운트 시작 버튼을 누르면 input창에 숫자 n++
버튼을 누르면 다시 못 누르게 비활성화

   <style>
        input{
            width: 300px;
            height: 300px;
            font-size: 200px;
            font-weight: bold;
            color: red;
            text-align: right;
        }
    </style>
    <script>
        let n=0; //프로그램이 가동되는 동안 누적시키기 위한 전역변수

        function setCount(){ //1씩 증가시키는 카운트 함수 정의
            n++
            //증가된 데이터를 화면에 출력
            document.querySelector("input").value=n; // 입력창 값이 변수로 바뀜
            setTimeout(setCount,100); //재귀호출 이용한 무한루프=자동호출
        };

        addEventListener("load", function(){ //문서가 로드되면... </html>까지 읽어들이면... 이란뜻
            let bt= document.querySelector("button");
            bt.addEventListener("click", function(){
                this.disabled=true; //버튼을 비활성화 시킴
                setCount();
            });
        });

    </script>
<body>
    <button>카운트 시작</button>
    <br>
    <input type="text">
</body>

내가 놓쳤던 부분

  • value가 의미하는 것
    <input> 입력된 텍스트, 숫자 등
    <textarea> 작성된 여러 줄 텍스트
    <select> 선택된 옵션의 값
    <button> 버튼의 값 (거의 안 씀

  • disabled 속성
    disabled = true 는 '사용자가 클릭하거나 입력하는 걸 막는다'라는 뜻

  • this.disabled = true; 는
    bt.disabled = true; 와 같은 의미
    둘 다 해당 버튼을 비활성화한다는 뜻

스톱워치


이번엔 start/stop 기능도 구현

  <style>
      input{
        width: 300px;
        height: 300px;
        text-align: center;
        font-size: 200px;
        color: palevioletred;
      }
      button{
        width: 100px;
        height: 50px;
        font-size: 25px;
      }
    </style>
    <script>
        let n=0;
        let timer;

        function setCount(){
          n++;
          document.querySelector("input").value=n; //value는 input에 적혀있는 값
          timer=setTimeout(setCount,100);
        }
      
      addEventListener("load", function(){
        let start=document.getElementById("start");
        let stop=document.getElementById("stop");
        
        start.addEventListener("click", function(){
          this.disabled=true;
          stop.disabled=false;
          // n=0; 이렇게 초기화 시켜주면 start 눌렀을 때 다시 1부터 시작
          setCount();
        });
        
        stop.addEventListener("click", function(){
          clearTimeout(timer); //카운트 멈춤
          this.disabled=true;
          start.disabled=false;
        });
      });

  </script>
<body>
  <input type="text">
  <br>
  <button id="start">Start</button>
  <button id="stop">Stop</button>
</body>

4. 감속도 운동 (부드러운 움직임 구현)

배운 내용

  • 감속도 운동
    (내 위치) = (현재 내 위치) + a*(목표지점 - 현재 내 위치 =남은거리 )
    x=x+a*(target-x)

  • a: 비율계수 (기울기). 숫자가 높을 수록 투박함

    비율계수가 높다 = 1에 가깝다 (예: 0.95, 0.98)
    매번 줄어드는 양이 아주 작아, 속도가 천천히 천천히 줄고
    줄어드는 느낌이 너무 느려서, 끝없이 질질 끄는 느낌이 생김.

    → 결과적으로 "부드럽다"기보다는 답답하고, 끈적끈적하고, 투박한 느낌이 남
    변화는 작지만 너무 느려서 깔끔하지 않고 투박하다는 뜻

    비율계수가 낮다 = 0에 가깝다 (예: 0.1, 0.2)
    매번 줄어드는 양이 커서 훅훅 줄어들고, 초반에는 변화가 빠르지만,
    일정 순간 이후 부드럽게 멈추는 느낌이 남

    → 이게 오히려 더 깔끔하고 자연스러운 감속처럼 보임

부드러운 움직임 구현

        let div;
        let a=0.01; //비율계수 크기. 숫자가 높을 수록 투박함
        let targetX=800; //도달할 목표지점

        function move(){ // 물체 움직임 함수
            //나의 위치=기존 나의 위치+a(비율계수/기울기)*(목표지점-나의 위치); 
            // 남은 거리에 정해진 비율로 증가할거야 = 야금야금 다가갈거야
            div.style.left=parseInt(div.style.left)+a*(targetX-parseInt(div.style.left))+"px";
            setTimeout(move(),10);
        }
        
        function creatRect(){ // 물체 생성용 함수 (Rectangle)
            div=document.createElement("div"); // 동적으로 <div>생성
            div.style.background="red";
            div.style.width=50+"px";
            div.style.height=50+"px";
            div.style.position="absolute";
            div.style.top=100+"px";
            div.style.left=0+"px";

            document.body.appendChild(div); //body는 1개. 별도 id 필요없음
        }


        addEventListener("load",function(){//프로그램 가동과 동시에 물체 생성
            creatRect();//물체 생성 함수 호출
            move();
        });
  • appendChild: 부모요소.appendChild(자식요소);
  • 감속도 운동
    나의 위치=기존 나의 위치+a(비율계수/기울기)*(목표지점-나의 위치);
    남은 거리에 정해진 비율로 증가할거야 = '야금야금 다가갈거야'라는 뜻
<script>
function move(){//물체 움직임 함수
 div.style.left=parseInt(div.style.left)+a*(targetX-parseInt(div.style.left))+"px";
}
</script>

여기서 parseInt(div.style.left)를 하는 이유는,
div.style.left의 값이 "0px", "120px" 이런 식으로 문자열(string) 이기 때문
parseInt()는 "0px", "120px"처럼 글자가 붙은 문자열을
숫자 0, 120으로 바꿔서 수학 계산이 가능하게 해줌

느낀 점

함수의 개념을 정확히 모르고 그냥 강사님을 따라 코딩할 때는 이 코드를 왜 여기 넣어야하는지 이 메서드는 왜 쓰는 건지 이해를 못하고, 일단 쓰라고 해서 썼었다. 답답하기도 답답하고 이걸 다 외워야하는 건가? 막막했었다.
그런데 함수의 개념을 조금이나마 이해한 지금은 어떤 의도로 이런 함수를 써야하는 지 왜 이 위치에 이 메서드를 써야하는지 아주 조금 감이 올 것 같다. VScode만 켜면 까막눈이 된 것 같아서 많이 답답했었는데, 이제는 GPT한테 대들 수 있을 정도(?)로 코드의 흐름이 보이기 시작한다. 어제보다 오늘 더, 오늘보다 내일 더 성장할 내 모습이 기대가 된다. 화이팅 ㅠㅠ

profile
아이들의 가능성을 믿었던 마음 그대로, 이제는 나의 가능성을 믿고 나아가는 중입니다.🌱

0개의 댓글