

배경을 왼쪽으로 움직이게 해서 캐릭터가 날아다니는 효과 구현 (left값 --)
캐릭터 대기 효과 (Idle 상태) 구현
방향키로 주인공 조작
총알도 구현
주인공을 정의한다
class Hero{
constructor(container,x,y,width,height,velX,velY){
//ES6의 클래스는 멤버변수를 생성자 안에 둬야함
this.container=container;
this.img=document.createElement("img");
this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.velX=velX;
this.velY=velY;
// 주입(injection) : 외부에서 전달된 데이터를 나의 객체에 보관하는 기법
//주인공의 sprite 이미지명 배열 선언 (프레임 한 컷 한 컷)
this.imgArray=[];
this.n=1; //이미지 배열의 index를 결정짓는 변수
for(let i=1; i<=18; i++){
this.imgArray.push("../images/hero/image"+i+".png")
}
//constructor 안에 적은 멤버변수는 객체가 살아있는 한 살아있음
//style
this.img.src="../images/hero/image1.png";
this.img.style.position="absolute";
this.img.style.left=this.x+"px";
this.img.style.top=this.y+"px";
this.img.style.width=this.width+"px";
this.img.style.height=this.height+"px";
//컨테이너에 부착
this.container.appendChild(this.img);
this.doIdle(); //움직이기 시작
}
//주인공 펄럭임 효과
//게임루프와 상관없이 자체적으로 끝없는 루프로 움직임 표현
doIdle(){
this.n++
this.img.src=this.imgArray[this.n];
if(this.n>=17)this.n=1; //이미지가 끝에 도달하면 다시 n을 1로 초기화
setTimeout( ()=>{
// 화살표함수는 this를 가질 수 없으므로, 여기서 this는 상위 스코프를 나타냄
this.doIdle(); //JS에서 this는 호출시점 결정
},50);
}
//모든 방향에 대한 움직임 동작 정의
move(){
this.x+=this.velX; //물리적 변화량
this.y+=this.velY; //물리적 변화량
//변화된 물리량을 화면에 반영(rendering)
this.img.style.left=this.x+"px";
this.img.style.top=this.y+"px";
}
}
총알을 정의한다
class Bullet{
constructor(container,x,y,width,height,velX,velY,bg){
this.container=container;
this.div=document.createElement("div");
this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.velX=velX;
this.velY=velY; //총알이 대각선으로 날아갈 수도 있으니까
this.bg=bg
// style
this.div.style.position="absolute";
this.div.style.left=this.x+"px";
this.div.style.top=this.y+"px";
this.div.style.width=this.width+"px";
this.div.style.height=this.height+"px";
this.div.style.background=this.bg;
this.div.style.borderRadius=50+"%"
// 총알의 테두리에 블러효과 주기
this.div.style.filter="blur(2px)"; //퍼짐 정도를 숫자로 표현
this.container.appendChild(this.div);
}
shoot(){ //총알이 날아가는 기능 정의
//만일 총알이 적군 등에 맞지 않고 화면 밖으로 나가는 경우,
//메모리 관리를 위해 제거 (화면제거+배열제거)
if(this.x>=1500){ //경계를 넘어서는 순간부터는...
//내가 총알이니 this.div
//화면에서 제거
this.container.removeChild(this.div);
//전역변수 접근 가능
//현재 총알인 내가 this
//배열 몇번 째에 위치해 있는지 위치를 알아내자
let index=bulletArray.indexOf(this);
//this는 현재 bullet객체를 가리킨다
bulletArray.splice(index,1);
}
this.x+=this.velX
this.y+=this.velY
this.div.style.left=this.x+"px";
this.div.style.top=this.y+"px";
}
}
<style>
#stage{
width: 1600px;
height: 576px;
background-image: url("../images/plane/bg.jpg");
background-size: 1600px 576px;
margin: auto;
position: relative;
overflow: hidden;
}
</style>
<div id="stage"></div>
<script src="./js/Hero.js"></script>
<script src="./js/Bullet.js"></script>
<script>
let bgX=0;
let hero;
let bulletArray=[]; //총알의 수를 예측할 수 없으므로 동적배열 (java.util.List와 동일)
let colorArray=["red","blue","navy","green","orange","yellow"];
function bgEffect(){
//stage의 배경의 left값 감소
let stage=document.getElementById("stage");
stage.style.backgroundPosition=`${bgX--}px 0px`;
}
function gameLoop(){
bgEffect();
hero.move();
//총알의 움직임 메서드 호출
for(let i=0; i<bulletArray.length; i++){
bulletArray[i].shoot();
}
}
function createHero(){
hero=new Hero(document.getElementById("stage"),100,100,200,200,0,0);
}
function fire(){ //총알 생성
// 누를 때마다 총알 생성시켜 이름 따로 주지 않고 배열에 담자
let bg=colorArray[parseInt(Math.random()*colorArray.length)];
let bullet=new Bullet(document.getElementById("stage"),(hero.x+hero.width*0.5),(hero.y+hero.height*0.5),30,30,10,2,bg)
bulletArray.push(bullet);
}
addEventListener("load", ()=>{
createHero();
//키보드 이벤트 연결
addEventListener("keydown", function(e){
switch(e.keyCode){//아스키 코드에 대한 판단
case 37: hero.velX=-10;break; //left
case 38: hero.velY=-10;break; //up
case 39: hero.velX=10;break; //right
case 40: hero.velY=10;break; //down
case 32: fire();break; //space
});
addEventListener("keyup", function(e){ //손 뗐을 때 움직이지 않게
switch(e.keyCode){//아스키 코드에 대한 판단
case 37: hero.velX=0;break; //left
case 38: hero.velY=0;break; //up
case 39: hero.velX=0;break; //right
case 40: hero.velY=0;break; //down
}
});
setInterval(gameLoop, 10);
});
</script>
자바스크립트 함수 정의 유형 3가지
1) 선언적 방법에 의한 함수선언 funciton test(){...}
2) 표현식에 의한 함수 선언 let test=function(){...}
3) 화살표 함수 (자바에서는 람다) let test =()=>{...}화살표함수는 this 보유할 수 없다
화살표 함수영역에서 사용되는 this는 나를 가리키는 것이 아니라
상위 스코프(영역)를 가리킴 (선언된 위치에서의 this를 상속받아 사용)
bulletArray에 들어 있는 모든 총알(bullet)에게 shoot() 메서드를 실행하는 루프
for(let i=0; i<bulletArray.length; i++){
bulletArray[i].shoot();
}
Bullet클래스의 shoot()메서드 中
if(this.x>=1500){ //경계를 넘어서는 순간부터는...
//내가 총알이니 this.div
//화면에서 제거
this.container.removeChild(this.div); // 1️⃣
//전역변수 접근 가능
//현재 총알인 내가 this
//배열 몇번 째에 위치해 있는지 위치를 알아내자
let index=bulletArray.indexOf(this); // 2️⃣
//this는 현재 bullet객체를 가리킨다
bulletArray.splice(index,1); // 3️⃣
}
메모리 관리를 위해 화면 밖에 생성된 총알 객체 제거하려면 화면제거 + 배열제거도 해야함
1️⃣ removeChild() 부모 요소에서 특정 자식 요소를 삭제하는 역할
기본 문법: 부모요소.removeChild(자식요소)
DOM에서만 제거(=화면에서만 제거). 실제로 요소를 메모리에서 삭제하는 것은 아님
<DOM 요소 참조 해제>
더 이상 참조하지 않도록 설정하려면
부모요소.removeChild(자식요소)
자식요소 = null;
이후 가비지 컬렉션(GC)이 알아서 메모리 해제
2️⃣ indexOf() : 문자열이나 배열에서 특정 요소(객체 포함)가 처음 등장하는 인덱스를 찾는 메서드
// 문자열에서의 indexOf()
let str = "JavaScript";
console.log(str.indexOf("a")); // 1 (처음 등장한 위치)
console.log(str.indexOf("Script")); // 4
console.log(str.indexOf("z")); // -1 (없으면 -1)
// 배열에서의 indexOf()
let arr = ["apple", "banana", "cherry"];
console.log(arr.indexOf("banana")); // 1
console.log(arr.indexOf("grape")); // -1
3️⃣ splice() : 배열에서 요소를 추가, 제거, 변경
⚠ splice( )는 배열을 직접 수정하므로, 원본 배열을 변경함
기본 문법: 배열명.splice(startIndex, deleteCount, item1, item2, ...)
¹ startIndex : 배열에서 변경을 시작할 인덱스
² deleteCount : 삭제할 아이템의 개수 (0이면 삭제 안 함)
³ item1, item2, ... : 배열에 추가할 아이템 (선택 사항)
| 구분 | 화면에서는 같음? | 내부 로직과 메모리는? |
|---|---|---|
this.div = null만 사용 | ✅ 같아 보일 수 있음 | ❌ 객체는 남고 로직은 계속 됨 |
splice()로 배열에서 제거 | ✅ 같음 | ✅ 완전 제거, 더 안전함 |
(중복된 데이터의 물리적 분리)
데이터 베이스 설계 시, 중복된 데이터가 존재할 경우 무결성이 깨질 수 있으므로, 중복된 데이터를 별도의 테이블로 '분리'시키는 설계 기법
정규화에 의해 물리적으로 분리된 테이블을 마치 하나의 테이블처럼 SELECT하는 쿼리 기법
물리적으로 분리된 EMP와 DEPT를 마치 하나의 테이블처럼 합쳐서 보여주자

SELECT ENAME, SAL, DNAME, LOC
FROM EMP, DEPT;
이 쿼리문으로 JOIN하면 아무런 조건이 없으므로 (N*M)개의 레코드가 조합
두 테이블의 공통이 되는 키 값을 이용하여 조건을 부여하면 정상적으로 레코드를 합칠 수 있음!
SELECT ENAME, SAL, DNAME, LOC
FROM EMP, DEPT
✔ WHERE EMP.DEPTNO=DEPT.DEPTNO;
✔ 조건: EMP의 DEPTNO와 DEPT의 DEPTNO가 같은 것만 골라서 합쳐라

--MySQL
CREATE TABLE MEMBER2(
MEMBER2_ID INT PRIMARY KEY AUTO_INCREMENT,
USER_ID VARCHAR(20) UNIQUE,
NAME VARCHAR(25) NOT NULL,
REGDATE TIMESTAMP DEFAULT NOW(),
GENDER CHAR(6) CHECK (GENDER='남' OR GENDER='여')
);
--ORACLE
--1.시퀀스 생성
CREATE SEQUENCE SEQ_MEMBER2
START WITH 1
INCREMENT BY 1
--2.테이블 생성
CREATE TABLE MEMBER2 (
MEMBER2_ID NUMBER PRIMARY KEY,
USER_ID VARCHAR2(20) UNIQUE,
NAME VARCHAR2(25) NOT NULL,
REGDATE DATE DEFAULT SYSDATE,
GENDER CHAR(6) CHECK (GENDER = '남' OR GENDER = '여')
);
-- 3. VALUE값 넣을 때 시퀀스 다음값 가져오기
SEQ_MEMBER3.NEXTVAL
--CHECK 제약조건에 이름을 붙이고 싶으면
CONSTRAINT CHK_CHECK CHECK (GENDER = '남' OR GENDER = '여')
);
PRIMARY KEY 설정 시 자동증가 방식에서
MySQL은AUTO_INCREMENT을
ORACLE은SEQUENCE + TRIGGER 조합을 사용
MySQL의 NOW() 대신
ORACLE은 SYSDATE 또는 SYSTIMESTAMP를 사용합니다.
CHECK 제약조건 문법은 비슷하게 사용
MySQL의 숫자타입 INT는 정수만
ORACLE의 숫자타입은 NUMBER 타입이 일반적 (정수,실수 가능)
Java의 비중이 커지면서 너무 정신이 없고 잠도 부족하다...ㅠㅠ
Java를 머리 속에 우겨넣느라 Front랑 DB쪽에 상대적으로 소홀해지는 것 같다ㅠㅠ 회고도 조금씩 밀리고... 이러면 안되는데 이번주는 교육받으러 3일밖에 가지 않아서 주말에 금방 커버가 가능하지만 5일 풀로 수업들을 때 이러면 안되는데 참 걱정이다 😢 그치만 이럴 때일수록 컨디션과 체력관리가 중요하다는 말을 많이 들었는데, 건강식도 챙겨먹고, 운동도 하고, 잠도 잘 수 있을 때 자놔야겠다!
걱정은 잠시 접고... 하면 된다!