body부분에 svg파일의 내용을 복사붙여넣기 한다.
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="./index.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Page</title>
</head>
<body>
<svg id="ufo-image" viewBox="0 0 2303 1630" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="gogo_UFO">
<path id="cloud"
d="M2303 340.921C2303 152.636 2150.37 0 1962.08 0C1773.79 0 1621.16 152.636 1621.16 340.921C1621.16 363.397 1623.34 385.368 1627.49 406.63C1571.43 446.827 1530.6 506.913 1515.32 576.574C1463.32 518.973 1388.06 482.771 1304.35 482.771C1227.61 482.771 1157.98 513.196 1106.85 562.635C1054.84 504.838 979.449 468.49 895.57 468.49C777.197 468.49 675.737 540.875 633.019 643.791C603.989 633.786 572.833 628.351 540.406 628.351C383.474 628.351 256.254 755.571 256.254 912.503C256.254 966.988 271.595 1017.89 298.182 1061.13C293.531 1060.9 288.854 1060.79 284.146 1060.79C127.22 1060.79 0 1188.01 0 1344.94C0 1501.87 127.22 1629.09 284.152 1629.09C414.988 1629.09 525.168 1540.66 558.195 1420.31C579.267 1425.3 601.246 1427.94 623.845 1427.94C670.796 1427.94 715.087 1416.55 754.109 1396.38C772.205 1464.1 833.98 1513.98 907.405 1513.98C995.034 1513.98 1066.07 1442.95 1066.07 1355.32C1066.07 1320.94 1055.13 1289.11 1036.54 1263.12C1106.05 1248.42 1166.19 1208.31 1206.77 1152.97C1213.41 1234.49 1281.67 1298.58 1364.9 1298.58C1435.64 1298.58 1495.56 1252.29 1516.03 1188.35C1547.15 1205.7 1583 1215.59 1621.16 1215.59C1740.57 1215.59 1837.38 1118.78 1837.38 999.367C1837.38 971.475 1832.09 944.819 1822.48 920.338C1957.42 906.371 2064.32 797.971 2075.97 662.353C2208.25 615.481 2303 489.267 2303 340.921Z"
fill="#0A0228" />
<path id="UFO-beam" d="M1143.5 497L1509 1474H1143.5H778L1143.5 497Z" fill="#9670BA" />
<path id="4-L"
d="M1009.41 922.58L993.14 922.958L989.815 1002.82L906.273 1010.2L910.713 924.809L812 927.02L839.356 764.734H909.22L888.892 877.862L913.282 877.125L920.31 737H1000.53L994.973 874.14L1009.39 873.403V922.561L1009.41 922.58Z"
fill="white" />
<path id="number-0"
d="M1250.25 878.957C1250.25 889.801 1249.04 900.589 1246.64 911.301C1244.24 922.013 1240.74 932.309 1236.19 942.171C1231.64 952.033 1226.05 961.215 1219.47 969.716C1212.88 978.218 1205.34 985.605 1196.84 991.896C1188.34 998.187 1179 1003.12 1168.84 1006.69C1158.68 1010.26 1147.74 1012.05 1136.02 1012.05C1124.31 1012.05 1113.16 1010.26 1102.94 1006.69C1092.72 1003.12 1083.37 998.187 1074.94 991.896C1066.5 985.605 1058.96 978.218 1052.31 969.716C1045.66 961.215 1040.05 952.033 1035.5 942.171C1030.94 932.309 1027.45 921.994 1025.05 911.206C1022.65 900.419 1021.44 889.669 1021.44 878.957C1021.44 868.245 1022.65 857.458 1025.05 846.613C1027.45 835.769 1030.93 825.416 1035.5 815.554C1040.05 805.692 1045.66 796.492 1052.31 787.915C1058.96 779.356 1066.52 771.932 1074.94 765.641C1083.39 759.349 1092.74 754.437 1103.04 750.848C1113.33 747.277 1124.33 745.482 1136.02 745.482C1147.72 745.482 1158.66 747.277 1168.84 750.848C1179 754.419 1188.34 759.349 1196.84 765.641C1205.34 771.932 1212.9 779.356 1219.47 787.915C1226.06 796.473 1231.64 805.692 1236.19 815.554C1240.74 825.416 1244.24 835.769 1246.64 846.613C1249.04 857.458 1250.25 868.245 1250.25 878.957V878.957ZM1172.24 884.512C1172.24 879.089 1171.5 873.044 1170.03 866.394C1168.56 859.744 1166.27 853.528 1163.19 847.728C1160.11 841.947 1156.16 837.073 1151.36 833.124C1146.57 829.176 1140.82 827.211 1134.17 827.211C1127.52 827.211 1121.61 829.176 1116.79 833.124C1111.99 837.073 1108.01 841.928 1104.87 847.728C1101.73 853.528 1099.41 859.744 1097.94 866.394C1096.46 873.044 1095.73 879.089 1095.73 884.512C1095.73 889.934 1096.46 895.979 1097.94 902.629C1099.41 909.279 1101.73 915.514 1104.87 921.295C1108.01 927.095 1111.99 931.95 1116.79 935.899C1121.59 939.847 1127.39 941.812 1134.17 941.812C1140.95 941.812 1146.55 939.847 1151.36 935.899C1156.16 931.95 1160.11 927.095 1163.19 921.295C1166.27 915.495 1168.56 909.279 1170.03 902.629C1171.5 895.979 1172.24 889.934 1172.24 884.512Z"
fill="white" />
<path id="4-R"
d="M1453 922.58L1436.73 922.958L1433.41 1002.82L1349.87 1010.2L1354.31 924.809L1255.61 927.02L1282.97 764.734H1352.83L1332.5 877.862L1356.89 877.125L1363.92 737H1444.14L1438.59 874.14L1453 873.403V922.561V922.58Z"
fill="white" />
<circle id="star_9" cx="1715.97" cy="724.932" r="14.1716" fill="white" />
<circle id="star_8" cx="621.172" cy="516.172" r="14.1716" fill="white" />
<circle id="star_7" cx="667.172" cy="1138.17" r="14.1716" fill="white" />
<circle id="star_6" cx="1814.82" cy="800.974" r="14.1716" fill="white" />
<circle id="star_5" cx="265.172" cy="829.172" r="14.1716" fill="white" />
<circle id="star_4" cx="565" cy="1073" r="9" fill="white" />
<circle id="star_3" cx="1844.55" cy="659.95" r="7.94992" fill="white" />
<circle id="star_2" cx="1883.95" cy="1001.95" r="7.94992" fill="white" />
<circle id="star_1" cx="463.95" cy="594.95" r="7.94992" fill="white" />
<path id="planet_2"
d="M1647 1190.69L1654.94 1180L1657.88 1193.01L1669.46 1186.5L1666.88 1199.59L1680.09 1198.37L1672.44 1209.27L1685 1213.56L1673.6 1220.39L1683.34 1229.45L1670.17 1231.03L1675.39 1243.28L1662.72 1239.33L1662.54 1252.67L1652.56 1243.88L1647 1256L1641.44 1243.88L1631.46 1252.67L1631.28 1239.33L1618.61 1243.28L1623.84 1231.03L1610.66 1229.45L1620.4 1220.39L1609 1213.56L1621.56 1209.27L1613.91 1198.37L1627.13 1199.59L1624.54 1186.5L1636.12 1193.01L1639.06 1180L1647 1190.69Z"
fill="white" />
<path id="planet_1"
d="M620.931 730.736L634.162 723L638.041 737.828L652.869 741.707L645.126 754.931L652.869 768.162L638.041 772.041L634.162 786.869L620.931 779.126L607.707 786.869L603.828 772.041L589 768.162L596.736 754.931L589 741.707L603.828 737.828L607.707 723L620.931 730.736Z"
fill="white" />
<g id="alien">
<g id="alien-head">
<circle id="Ellipse 24" cx="1156.53" cy="435.375" r="70.5" transform="rotate(7.76141 1156.53 435.375)"
fill="#B5F7C3" />
<circle id="Ellipse 25" cx="1143.86" cy="399.328" r="14.9294" transform="rotate(7.76141 1143.86 399.328)"
fill="white" />
<circle id="Ellipse 26" cx="1178.38" cy="404.033" r="14.9294" transform="rotate(7.76141 1178.38 404.033)"
fill="white" />
<line id="Line 5" x1="1133.66" y1="382.034" x2="1192.83" y2="390.099" stroke="black" stroke-width="11.6118"
stroke-linecap="round" />
<line id="Line 6" x1="1159.36" y1="402.278" x2="1157.35" y2="417.071" stroke="#09A82D" stroke-width="11.6118"
stroke-linecap="round" />
<line id="Line 7" x1="1134.45" y1="431.529" x2="1164.03" y2="435.561" stroke="#5EDF7A" stroke-width="16.5882"
stroke-linecap="round" />
<line id="Line 8" x1="1136.09" y1="431.753" x2="1160.75" y2="435.113" stroke="#09A82D" stroke-width="3.31765"
stroke-linecap="round" />
<circle id="left-eye" cx="1137.38" cy="399.282" r="5.18382" transform="rotate(7.76141 1137.38 399.282)"
fill="black" />
<circle id="right-eye" cx="1171.89" cy="403.987" r="5.18382" transform="rotate(7.76141 1171.89 403.987)"
fill="black" />
<circle id="Ellipse 30" cx="1193.69" cy="421.324" r="1.65417" transform="rotate(7.76141 1193.69 421.324)"
fill="#5EDF7A" />
<circle id="Ellipse 33" cx="1119.23" cy="410.376" r="1.65417" transform="rotate(7.76141 1119.23 410.376)"
fill="#5EDF7A" />
<circle id="Ellipse 31" cx="1187.37" cy="432.455" r="1.65417" transform="rotate(7.76141 1187.37 432.455)"
fill="#5EDF7A" />
<circle id="Ellipse 34" cx="1112.92" cy="421.507" r="1.65417" transform="rotate(7.76141 1112.92 421.507)"
fill="#5EDF7A" />
<circle id="Ellipse 32" cx="1199.79" cy="429.35" r="1.65417" transform="rotate(7.76141 1199.79 429.35)"
fill="#5EDF7A" />
<circle id="Ellipse 35" cx="1125.33" cy="418.403" r="1.65417" transform="rotate(7.76141 1125.33 418.403)"
fill="#5EDF7A" />
</g>
<path id="alien_hand-R" d="M1077.07 400.9C1078.47 405.192 1082.03 412.875 1092.04 416.39" stroke="#B5F7C3"
stroke-width="12" stroke-linecap="round" />
<path id="alien_hand-L" d="M1225.41 436.218C1229.9 435.767 1240.03 429.375 1243.72 424.875" stroke="#B5F7C3"
stroke-width="12" stroke-linecap="round" />
</g>
<g id="UFO">
<path id="Vector"
d="M1193.96 510.931C1178.99 486.957 1152.37 471 1122.01 471C1075.31 471 1037.41 508.775 1037.23 555.454C1015.66 572.125 997.298 592.712 983.194 616.171C990.272 605.374 1011.08 591.984 1040.4 578.82C1062.87 568.738 1090.33 558.788 1120.44 550.227C1150.55 541.68 1179.14 535.713 1203.56 532.498C1235.85 528.237 1260.83 528.767 1272.32 534.429C1248.44 521.992 1221.99 513.816 1193.96 510.918V510.931Z"
fill="#FFD12E" />
<path id="Vector_2"
d="M1278.95 541.217C1278.25 538.743 1276.33 536.666 1273.38 534.998C1273.03 534.813 1272.68 534.628 1272.33 534.443C1260.85 528.767 1235.87 528.237 1203.57 532.511C1179.16 535.739 1150.57 541.693 1120.45 550.241C1090.34 558.788 1062.88 568.738 1040.42 578.833C1011.08 591.984 990.259 605.374 983.18 616.171C982.85 616.726 982.519 617.295 982.188 617.851C980.64 620.749 980.137 623.435 980.825 625.856C984.146 637.565 1014.11 640.172 1056.68 634.509C1060.87 648.865 1074.13 659.37 1089.85 659.37C1103.93 659.37 1116.03 650.942 1121.42 638.862C1125.8 640.635 1130.59 641.614 1135.61 641.614C1148.37 641.614 1159.63 635.316 1166.51 625.657C1171.93 629.19 1178.4 631.254 1185.36 631.254C1204.44 631.254 1219.92 615.787 1219.92 596.695C1219.92 593.85 1219.58 591.098 1218.93 588.452C1258.15 570.907 1282.29 552.926 1278.96 541.217H1278.95Z"
fill="#FF9C27" />
</g>
<g id="alien_Leg-L">
<path id="Vector 5" d="M1167.32 481.051C1162.81 481.266 1152.72 483.64 1148.44 491.418" stroke="#09A82D"
stroke-width="12" stroke-linecap="round" />
<path id="Vector 9" d="M1142.36 487.071L1147.87 490.931" stroke="#09A82D" stroke-width="12"
stroke-linecap="round" />
</g>
<g id="alien_Leg-R">
<path id="Vector 5_2" d="M1105.5 470C1100 471 1094 473.5 1090.43 479.918" stroke="#09A82D" stroke-width="12"
stroke-linecap="round" />
<path id="Vector 9_2" d="M1084.36 475.571L1089.87 479.432" stroke="#09A82D" stroke-width="12"
stroke-linecap="round" />
</g>
</g>
</svg>
</body>
</html>
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #24145E;
}
따라서 이 CSS 코드의 전체적인 효과는 전체 화면을 채우는, 스크롤 없는, 배경색이 #24145E인 body를 만드는 것이다.

이제 행성 하나를 회전시켜 보자.
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #24145E;
}
@keyframes rotation {
to {
transform: rotateZ(360deg);
}
}
#planet_2 {
animation-name: rotation;
animation-duration: 4s;
animation-iteration-count: infinite;
}

우리가 만들고 싶은건 제자리에서 회전하는 행성이었지만, 공전하는 행성을 볼 수 있는데, 이것은 svg파일에 정의된 중심을 중심으로 회전하기 때문이다.
transform-box를 사용하여, 변환의 기준점을 바꿔보자.
transform-box: fill-box; : 변형(transform)의 원점을 결정하는 속성이다. fill-box 값은 SVG 요소에 대해 적용되며, 그 요소의 view box가 transform의 원점이 된다.
#planet_2 {
animation-name: rotation;
animation-duration: 4s;
animation-iteration-count: infinite;
transform-box: fill-box;
}

하지만, 제자리에서 자전하는 움직임이 아니다.
SVG의 viewBox 속성은 4개의 값(min-x, min-y, width, height)을 가진다. 여기서 min-x와 min-y 값은 viewBox의 왼쪽 상단 점을 나타내며, 이 점이 SVG의 원점(0,0)이 된다.
즉, transform-box: fill-box;를 설정하면, transform 속성이 적용될 때, 이 변형의 원점은 viewBox의 왼쪽 상단 점인 (min-x, min-y) 위치가 된다.
예를 들어, viewBox="0 0 100 100"의 경우, SVG의 원점은 viewBox의 왼쪽 상단 코너인 (0,0)이 된다. 여기에서 수행되는 모든 변환(예: translate, rotate, scale 등)은 이 원점을 기준으로 적용된다.
따라서, transform-box: fill-box; 만 적용시키면, 왼쪽 상단을 기준으로 회전하는 움직임이 나온다.
요소의 중앙에서 회전시키기 위해, CSS의 변환에 대한 기준점을 요소의 중앙으로 잡아햐 한다.
'tranform-origin: center;'를 쓰면 가능하다.
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #24145E;
}
@keyframes rotation {
to {
transform: rotateZ(360deg);
}
}
#planet_2 {
animation-name: rotation;
animation-duration: 4s;
animation-iteration-count: infinite;
transform-box: fill-box; /*변환의 기준점을 결정*/
transform-origin: center; /*변환 기능에 대한 기준점을 설정*/
}

같은 방식으로 planet_1에 에니메이션 시간만 수정한 뒤에 적용시켜보자.
#planet_1 {
animation-name: rotation;
animation-duration: 2s;
animation-iteration-count: infinite;
transform-box: fill-box;
transform-origin: center;
}
#planet_2 {
animation-name: rotation;
animation-duration: 4s;
animation-iteration-count: infinite;
transform-box: fill-box;
transform-origin: center;
}

이제, 각각의 별들이 반짝이게 만들기 위해, 애니메이션으로 축소와 확대를 반복해보자.
/*작아졌다 커지는 애니메이션*/
@keyframes shrinkAndGrow {
0% {
transform: scale(0);
}
25% {
transform: scale(0.5);
}
50% {
transform: scale(0);
}
75% {
transform: scale(1)
}
100% {
transform: scale(0);
}
}
각각의 모든 별들에게 @keyframes shrinkAndGrow 를 적용시키자.
#star_1 {
animation: shrinkAndGrow 10s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_2 {
animation: shrinkAndGrow 2s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_3 {
animation: shrinkAndGrow 8s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_4 {
animation: shrinkAndGrow 9s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_5 {
animation: shrinkAndGrow 15s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_6 {
animation: shrinkAndGrow 3s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_7 {
animation: shrinkAndGrow 4s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_8 {
animation: shrinkAndGrow 11s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}
#star_9 {
animation: shrinkAndGrow 7s infinite; /*축약형: 이름, 지속, 횟수*/
transform-box: fill-box;
transform-origin: center;
}

UFO의 빔을 X방향으로 축소와 확대를 하는 애니메이션을 제작해보자.
@keyframes expandHorizontal {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
#UFO-beam {
animation: expandHorizontal 5s;
transform-box: fill-box;
transform-origin: center;
}

외계인의 팔이 흔들리도록 애니메이션을 제작해보자.
/*외계인 팔 흔드는 애니메이션*/
@keyframes shakingUpAndDown {
from {
transform: translateY(0);
}
25% {
transform: translateY(5px);
}
50% {
transform: translateY(-5px);
}
75% {
transform: translateY(5px);
}
to {
transform: translateY(0);
}
}
#alien_hand-R {
animation: shakingUpAndDown 2s infinite;
transform-box: fill-box;
transform-origin: center;
}
#alien_hand-L {
animation: shakingUpAndDown 2s infinite;
transform-box: fill-box;
transform-origin: center;
}

<script>
document.getElementById('number-0').addEventListener("animationend", () => {
document.getElementById('number-0').setAttribute("id", "number-0-soaking")
})
</script>
이 JavaScript 스크립트는 animationend 이벤트에 대한 리스너를 HTML 요소에 추가하는 역할을 한다. 여기서 animationend 이벤트는 CSS 애니메이션이 완료되었을 때 발생한다.
스크립트의 세부 내용은 다음과 같습다.
이 스크립트를 사용하면, 특정 요소가 애니메이션을 완료하면 그 요소의 id를 변경하여 다른 스타일 또는 동작을 적용할 수 있다.
@keyframes soaking {
to {
transform: translateY(-250px) scale(0);
}
}
#number-0-soaking {
animation: soaking 2s;
}
따라서 이 코드는 id가 'number-0-soaking'인 요소가 화면에서 위로 250px만큼 이동하면서 점점 사라지는 애니메이션을 적용하는 것을 의미한다.
JavaScript 코드에서 이전에 봤던 것처럼, animationend 이벤트가 발생할 때 'number-0' 요소의 id가 'number-0-soaking'으로 바뀌므로, 이 CSS 코드는 그 요소에 적용된다. 그 결과, 해당 요소는 2초 동안 위로 이동하며 사라지게 된다.

하지만 코드를 마치고 오류가 발생한다.

이 오류 메시지는 JavaScript가 요소를 찾지 못했을 때 발생한다. document.getElementById('number-0') 이 부분에서 'number-0'라는 ID를 가진 요소를 찾으려 했지만 해당 요소가 없어 null을 반환받았다. 그리고 이후에 setAttribute 메서드를 호출하려 했으나, null 값에는 메서드를 호출할 수 없어서 TypeError가 발생한 것이다.
원인은, 'number-0'가 'number-0-soaking'이란 아이디로 바뀌면서, 'number-0'를 찾지 못해 발생하는 오류이다.
이 오류는 다음과 같이 스크립트를 수정하면 해결된다.
<script>
const setZeroDisappear = () => {
document.getElementById('number-0').removeEventListener("animationend", setZeroDisappear)
document.getElementById('number-0').setAttribute("id", "number-0-soaking")
}
document.getElementById('number-0').addEventListener("animationend", setZeroDisappear)
</script>
이 스크립트는 다음과 같은 동작을 수행한다.
즉, 'number-0'이라는 ID를 가진 요소에 애니메이션이 적용되어 있고, 해당 애니메이션이 종료되면 그 요소의 ID를 'number-0-soaking'으로 변경하며, 'animationend' 이벤트 리스너를 제거하는 동작을 수행한다.
이를 통해 한번 애니메이션이 끝나고 나서는 더 이상 'animationend' 이벤트를 감지하지 않도록 할 수 있다. 이는 특정 애니메이션이 한 번만 실행되어야 할 경우에 유용하다. 또한, 요소의 ID를 변경함으로써 CSS를 통해 해당 요소에 다른 스타일을 적용할 수도 있다.
/*UFO 안쪽으로 숫자 0 이 빨려들어가는 애니메이션*/
@keyframes soaking {
to {
transform: translateY(-250px) scale(0);
}
}
/*UFO빔을 사라지게 만듬.*/
@keyframes shrinkHorizontal {
to {
transform: scaleX(0);
}
}
#number-0-soaking {
animation: soaking 2s;
transform-box: fill-box;
transform-origin: center;
}
#UFO-beam-disappearing {
animation: shrinkHorizontal;
}

이제 UFO에 숫자 0이 빨려들어갈 때, UFO-beam이 사라지는 애니메이션 효과를 줘본다.
#UFO-beam-disappearing {
animation: shrinkHorizontal 2s;
transform-box: fill-box;
transform-origin: center;
}
/*UFO빔을 사라지게 만듬.*/
@keyframes shrinkHorizontal {
to {
transform: scaleX(0);
}
}
<script>
const setZeroAndBeamDisappear = () => {
document.getElementById('number-0').removeEventListener("animationend", setZeroAndBeamDisappear)
document.getElementById('number-0').setAttribute("id", "number-0-soaking")
document.getElementById('UFO-beam').setAttribute("id", "UFO-beam-disappearing")
}
document.getElementById('number-0').addEventListener("animationend", setZeroAndBeamDisappear)
</script>
</html>
스크립트 코드는 HTML 요소에 애니메이션이 끝날 때 발생하는 "animationend" 이벤트에 반응하는 이벤트 리스너를 추가하는 역할을 한다. 이벤트 리스너는 "setZeroAndBeamDisappear"라는 함수이며, 이 함수가 호출되면 다음 작업이 수행된다.
따라서 이 스크립트는 "number-0" 요소의 애니메이션이 끝나면 "number-0" 요소와 "UFO-beam" 요소의 스타일 또는 애니메이션을 변경하는 역할을 한다. 이는 id를 변경함으로써 CSS 선택자가 매칭되는 요소를 바꾸는 방식으로 이루어진다.

이제, 0과 빔이 사라져도 애니메이션이 무한반복되게 만들어보자.
<script>
const setZeroAndBeamAppear = () => {
document.getElementById('number-0-soaking').removeEventListener("animationend", setZeroAndBeamAppear)
document.getElementById('number-0-soaking').setAttribute("id", "number-0")
document.getElementById('UFO-beam-disappearing').setAttribute("id", "UFO-beam")
document.getElementById('number-0').addEventListener("animationend", setZeroAndBeamDisappear)
}
const setZeroAndBeamDisappear = () => {
document.getElementById('number-0').removeEventListener("animationend", setZeroAndBeamDisappear)
document.getElementById('number-0').setAttribute("id", "number-0-soaking")
document.getElementById('UFO-beam').setAttribute("id", "UFO-beam-disappearing")
document.getElementById('number-0-soaking').addEventListener("animationend", setZeroAndBeamAppear)
}
document.getElementById('number-0').addEventListener("animationend", setZeroAndBeamDisappear)
</script>

이 스크립트는 두 개의 함수, setZeroAndBeamAppear와 setZeroAndBeamDisappear를 정의하고 있다. 이 두 함수는 각각 'number-0'와 'UFO-beam'이라는 ID를 가진 요소의 애니메이션 종료 이벤트에 연결되어 있다.
setZeroAndBeamDisappear 함수는 'number-0' 요소의 애니메이션이 종료되면 호출된다. 이 함수에서는
setZeroAndBeamAppear 함수는 'number-0-soaking' 요소의 애니메이션이 종료되면 호출된다. 이 함수에서는:
CSS의 transform-box 속성은 transform 속성(rotate, scale, skew 등)이 적용될 때 변환의 기준점을 결정한다.
이 속성은 다음과 같은 값들을 가진다.
transform-box 속성의 기본값은 border-box이다.
transform-origin 속성은 CSS의 변환(transform) 기능에 대한 원점(기준점)을 설정한다. 이 속성은 rotate, scale, skew와 같은 변환을 적용할 때 중심점이 어디인지 정의한다.
transform-origin의 값은 세 가지 형태로 지정될 수 있다.
기본적으로 transform-origin의 값은 50% 50%이므로, 변환은 요소의 중앙에서 발생한다.

'addEventListener'와 'removeEventListener'는 웹 브라우저의 JavaScript에서 이벤트를 처리하기 위한 중요한 메서드들이다. 이벤트는 사용자가 웹사이트와 상호작용하는 방식을 나타낸다. 예를 들어 클릭, 스크롤, 키보드 입력 등이 있다.
'addEventListener' 메서드는 특정 이벤트가 발생했을 때 호출할 함수를 등록하는 데 사용된다. 이 메서드는 2개 또는 3개의 인자를 받는다.

type: 제거할 이벤트의 타입을 나타낸다. 예를 들어, "click", "mouseover", "keydown", "animationend" 등이 이에 해당한다.
listener: 제거할 이벤트 리스너 함수를 나타낸다. 이 함수는 해당 이벤트가 발생했을 때 실행되는 콜백 함수다. 이전에 addEventListener 메소드를 통해 등록한 동일한 함수를 지정해야 한다. 그래야 이벤트 리스너를 정확히 찾아 제거할 수 있다.
removeEventListener 메서드는 특정 이벤트에 대한 이벤트 핸들러를 제거하는 데 사용된다. 이 메서드는 addEventListener에서 등록했던 것과 같은 인자들을 받는다. 다시 말해, 제거하려는 이벤트의 종류와 제거하려는 이벤트 핸들러 함수를 넘겨줘야 한다. 이 메서드를 사용하면 이전에 등록했던 이벤트 핸들러를 취소할 수 있다.
이 두 메서드를 사용하면 웹페이지에서 발생하는 이벤트를 동적으로 제어할 수 있다. 예를 들어, 특정 버튼을 클릭하면 애니메이션이 실행되고, 다른 버튼을 클릭하면 애니메이션이 중지되도록 할 수 있다. 이런 방식으로 웹페이지는 사용자의 입력에 따라 반응하게 된다.
const name = () => {}과 같은 형태로 선언된 함수는 "화살표 함수(Arrow Function)"라고 부른다. JavaScript의 ES6(ECMAScript 2015) 버전에서 도입된 이 새로운 함수 선언 방식은 보다 간결하고, this를 렉시컬(lexical) 컨텍스트에 바인딩하는 등의 특징이 있다.
기존의 함수 선언 방식과 화살표 함수의 차이를 비교해보면, 다음과 같다.
//일반 함수
function name() {
// 함수 내용
}
//화살표 함수
const name = () => {
// 함수 내용
}
JavaScript에서 this는 일반적으로 현재 컨텍스트를 가리킨다. this의 값은 함수가 호출되는 방법에 따라 달라진다. 일반 함수에서 this를 사용하면, 그 함수가 어떻게 호출되었는지에 따라 this의 값이 달라진다.
하지만, 화살표 함수에서의 this는 일반 함수와는 다르게 작동한다. 화살표 함수에서 this를 사용하면, 그 this는 렉시컬 컨텍스트, 즉 화살표 함수가 선언된 범위의 this를 가리킨다.
const obj = {
value: 'Hello, World!',
print: function() {
console.log(this.value); // "Hello, World!"
}
}
obj.print(); // "Hello, World!"
위 코드에서는 print 메소드가 this.value를 출력한다. 이 경우 this는 obj 객체를 가리킨다.
그런데 다음과 같이 화살표 함수를 사용하면 this의 값이 달라진다.
const obj = {
value: 'Hello, World!',
print: () => {
console.log(this.value); // undefined
}
}
obj.print(); // undefined
위 코드에서는 print 메소드가 화살표 함수로 정의되었기 때문에 this는 렉시컬 컨텍스트를 가리키며, 이 경우 this.value는 undefined이다.
이런 특징 때문에 화살표 함수는 특히 이벤트 핸들러, 타이머 콜백 등에서 유용하게 사용된다. this가 렉시컬 컨텍스트에 바인딩되므로, 함수를 선언한 객체의 컨텍스트를 잃지 않고 사용할 수 있다.
const obj = {
value: 'Hello, World!',
print: () => {
value: 'Not hello'
console.log(this.value);
}
}
obj.print();
그럼 위와 같은 코드는 정상적으로 작동할까?
JavaScript에서 화살표 함수는 자신의 this 값을 가지지 않는다. 그 대신, 화살표 함수는 this를 포함하는 외부 함수(렉시컬 컨텍스트)에서 this 값을 가져온다. 그러나 제시된 예제에서는 this가 어떠한 함수로부터도 포함되지 않았기 때문에, this는 전역 객체(global object)를 가리킨다.
따라서 obj.print();를 호출하면, console.log(this.value);에서 this는 전역 객체를 참조하므로 전역 스코프에 value라는 이름의 변수가 없다면 undefined를 출력한다. 만약 전역 스코프에 value라는 변수가 존재한다면 그 변수의 값을 출력하게 될 것이다.
이 경우 obj 객체의 value 속성을 출력하려면 다음과 일반 함수를 사용해야 한다.
const obj = {
value: 'Hello, World!',
print: function() {
console.log(this.value);
}
}
obj.print(); // 출력: 'Hello, World!'
위 코드에서 print 메소드는 일반 함수로 정의되어 있다. 일반 함수는 자신이 호출된 컨텍스트에서 this 값을 가져오므로 this.value는 obj 객체의 value 속성을 가리킨다. 따라서 obj.print();를 호출하면 'Hello, World!'를 출력하게 된다.
렉시컬 컨텍스트 또는 렉시컬 환경은 프로그래밍 언어에서 코드가 작성된 위치에 따라 변수, 함수, 객체 등의 유효 범위(scope)를 결정하는 매커니즘을 말한다. "렉시컬"이란 단어는 사전적, 문법적인 순서나 구조를 의미하는데, 여기서는 코드가 작성된 위치를 가리킨다.
JavaScript는 렉시컬 스코핑을 사용하는 언어다. 이는 변수의 유효 범위가 코드가 작성된 시점에 결정된다는 것을 의미한다. 즉, 함수가 어디에서 호출되는지가 아니라 어디에서 선언되었는지에 따라 유효 범위가 결정된다.
let x = 1; // 전역 변수
function outerFunction() {
let x = 2; // outerFunction 스코프의 변수
function innerFunction() {
console.log(x); // ?
}
innerFunction();
}
outerFunction(); // 출력: 2
innerFunction에서 x를 출력하라고 했을 때, 이 x의 값은 무엇일까? innerFunction은 outerFunction 안에서 선언되었기 때문에, 렉시컬 스코핑에 따라 outerFunction의 스코프에서 x를 찾는다. 따라서 2가 출력된다. innerFunction이 어디에서 호출되었는지는 중요하지 않고, 어디에서 선언되었는지가 중요하다. 이것이 바로 렉시컬 컨텍스트다.
컴퓨터 과학에서 컨텍스트(context)란 현재 코드가 실행되고 있는 상황을 나타내는 정보를 말한다. 이는 프로그램이나 함수가 어떻게 동작해야 하는지를 결정하는데 중요한 역할을 한다.
컨텍스트는 프로그램이 어떤 작업을 수행하고 있는지, 어떤 변수들이 사용되고 있는지, 현재 실행 중인 함수는 무엇인지 등의 정보를 포함한다. 이는 프로그램의 실행 흐름과 상태를 관리하고, 프로그램의 동작을 제어하는데 필요한 정보를 제공한다.
자바스크립트에서는 함수의 컨텍스트에는 'this'라는 키워드가 포함되는데, 'this'는 함수가 어떻게 호출되었는지에 따라 그 값이 바뀐다. 'this'의 값은 일반적으로 함수가 속한 객체를 가리키지만, 함수가 어떻게 호출되었는지에 따라서 그 값이 변경될 수 있다.
또한, 클로저라는 개념은 함수와 그 함수가 선언된 렉시컬 컨텍스트의 조합으로, 이 렉시컬 컨텍스트는 클로저가 생성된 위치의 스코프 체인이다. 이러한 특성으로 인해 클로저는 자신이 생성된 컨텍스트 밖에서도 해당 컨텍스트에 있는 변수에 접근할 수 있다.
따라서 컨텍스트는 코드의 실행 상황을 설명하는 데 있어 매우 중요한 개념이다.