카카오톡 모아보내기를 대충 구현해보자
여러분들 카톡할때 사진 모아서 보내기 하시죠? 그걸 구현해볼건데, 이게 사진 장 수에 따라서 구성이 어떻게 되냐면
3장: 3*1
4장: 2*2
5장: 3*1+2*2
10장: 3*2+2*2
이렇게 된다. 10은 3*3+1로 해도 되지 않아요? 그렇게 해도 되는데 카톡 모아서 보내기는 사진 한장만 보낼 때 빼고는 밑에 한장만 띡 안 띄우고 3배수+2배수로 한다.
아, 근데 사진을 매번 만들기도 힘들고 해서 일단은 div를 동적으로 만드는걸로 나 자신이랑 합의 보기로 했음. 오늘은 감도 안 잡혀서 걍 GPT한테 해달라고 했다. 고수 코더 여러분들은 따라하지 마세여...
const photoContainer = document.querySelector('.photocontainer');
const photoCount = document.querySelector('#photocount');
const photoButton = document.querySelector('#photobutton');
photoButton.addEventListener('click',(e)=>{
let countNum = photoCount.value;
console.log(countNum)
});
일단 동적으로 생성할 div의 수를 입력받아보자. HTML? CSS? 그게 지금 중요한 게 아녀… 이 코드의 코어이자 빡셈을 담당하는 자바스크립트를 봐야 한다 이거요.
사진이 한 장일때
일단 사진이 한 장일때를 빼면 무조건 3n+2n으로 들어가는 구조인데, 여기에 맞춰서 div를 우리가 동적으로 생성해야 한다. 이거 어디서 비슷한 문제 봤다고요? 예에에에에에에에전에 그리디 알고리즘 풀 때 봤던 것 같죠? 거스름돈 640원을 가장 적은 수의 동전으로 지불하려면 어떻게 해야 할 지. 뭐 그리디 알고리즘이 전체적으로 최적일거라는 보장은 없지만, 사진 올린거 배치하는데 전체적으로 최적이고 자시고가 어디 있음.
if (photoRemain == 1) {
const photoRow = document.createElement('div');
photoRow.className = 'grid-row';
photoContainer.appendChild(photoRow);
}; //사진이 한 장만 있을 때
일단 사진이 한 장일때를 따로 처리해야 한다. 사진이 하나밖에 없으면 3n+2n으로 못 하거든… 3n+2n에 0을 때려박지 않는 이상 1이 더 작아요. 아무튼 그래서 예외처리 한다 보시면 됩니다.
이건 나중에 디스플레이 플렉스 줘서 잡으면 되니까 안심하십시오.
사진이 두 장 이상일때
왜 두장부터냐고요? 3n+2n에서 3n이 0이면 두장 할 수 있잖아.
while (photoRemain > 0) {
let photosInrow;
if (photoRemain >= 3) {
photosInrow = 3;
} else if (photoRemain == 2) {
photosInrow = 2;
} else {
console.log('어? 뭔가 이상한데?');
break;
}
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let i = 0;i < photosInrow;i++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
photoRemain -= photosInrow;
}
자바스크립트에도 while문이 있냐고? 있다. 잘 안 쓸 뿐이지. 아무튼 사진이 2장 이상이면 3x+2y꼴로 나타내라는 얘기인데... 이게 문제가 있어요.
일단 이게 첫번째 문제다. 최대 3장씩 가로로 나열돼야 맞는데 저기 보시면 세로로 줄줄이 있죠? 이거는 JS가 아니라 CSS를 건드려서 해결 봐야 하는 문제라서 디스플레이를 flex로 주면 된다. 근데 저게 첫 번째 문제면, 다른 문제가 또 있다는건가요? 그렇다.
사진은 13장인데 한장이 짤렸다. 내가 지피티한테 3n+2n으로 나와야 한다고 했는데… 얘 더워먹었나…
if (photoRemain == 1) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
photoContainer.appendChild(photoInbox);
return;
}; //사진이 한 장만 있을 때
지피티는 가끔 말을 해줘도 씹어먹을 때가 있어서 매번 리마인드를 해 줘야 한다는 단점이 있다. 얘 그림 그릴때도 A 수정하면 B 망치고 B 수정하면 다시 A 망치고 반복함… 실화예요. 이것때문에 이미지 리사이징 포기하고 구글 whisk에게 그려달라고 했다니까.
그래서 뭘 어떻게 바꾼건데요? 일단 사진 한장짜리는 크게 바뀐 거 없다. 인박스 안에 사진이 들어가는 구조긴 한데 그게 다거든. 그래서 크게 바꿀 건 2장 이상 말고는 없다.
while (photoRemain > 0) {
let photosInrow;
if (photoRemain % 3 === 1 && photoRemain >= 4) {
photosInRow = 2; // 1장 남을 미래 방지
} else if (photoRemain >= 3) {
photosInRow = 3;
} else if (photoRemain == 2) {
photosInRow = 2;
} else {
console.log('어? 뭔가 이상한데?');
break;
}
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let i = 0; i < photosInRow; i++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
photoRemain -= photosInRow;
}
얘는 대체 뭔 미래를 방지한다는건지 모르겠음... 시키는거나 똑띠 했으면 좋겠구만.
일단 while문의 if문 맨 위를 보자. 저 문제가 터진게 4, 7, 10인데 1을 제외하면 3n+1이라는 공통점이 있다. 근데 내가 지피티한테 예시로 설명까지 들어가면서 설명했던 배치는 '2장 이상이면 무조건 3x+2y 형태로 배치가 되어야 한다'였고, 저대로라면 4장은 2+2, 7장은 3+2+2, 10장은 3+3+2+2가 되어야 하는데 내 요청을 또 무시하고 3n+1로 만들어버린 것. if문 맨 위는 사진 장 수를 3으로 나누기했을 때 1이 남았거나(3n+1) 4장이면 사진을 한 줄에 두 장 넣으라는 얘기이다. 7장에서 두장 빼면 5장은 3n+2니까 기존 로직으로 해결이 된다 이거지.
그럼 이걸로 끝인가요? 아뇨.
내가 표현식을 3x+2y로 쓴 건 3장짜리가 먼저 올라오기 때문이다. 그니까 사진이 7장이면 3+2+2, 10장이면 3+3+2+2, 13장이면 3+3+3+2+2 이런 식으로. 근데 지금 보시면 7장이 2+3+2로 올라오잖음? 근데 이것까지는 내가 지시를 안 했으니 그럴 수 있다 치자.
let photoRemain = countNum;
let photoIndex = 1;
if (photoRemain == 1) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
photoContainer.appendChild(photoInbox);
return;
}; //사진이 한 장만 있을 때
if (photoRemain === 4) {
for (let i = 0; i < 2; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
return;
}//4장은 2+2
// 그 외 케이스
let threeRows = Math.floor(photoRemain / 3);
let extra = photoRemain % 3;
// 3장씩 먼저 배치
for (let i = 0; i < threeRows; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 3; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
// 3n + 1 → 2 + 2 배치
if (extra === 1) {
for (let i = 0; i < 2; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
}
// 3n + 2 → 2장 배치
else if (extra === 2) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
저게 다 뜨는 게 아니고 3n+1… 1, 7, 10에서 뜬 걸 확인했다. 4는 원래 4장이라 패스. 내가 저거 실행해보고 하도 얼척없어서 지피티한테 이게 맞냐고 물었음.
사실 코드가 길고 복잡해보이지만 전체적으로 보면 사진 장수를 3장씩 먼저 올리고 남은걸 두 장씩 올리면 되는 매우 간단한 코드이다. 근데 뭐가 문제임? 이게 3n+2(5, 8, 11)장일때는 3장을 n줄씩 올리고 밑에 두 장이 들어가면 되는데, 3n+1이 되니까 나머지를 계산하는 과정에서 문제가 생긴 것이다.
let threeRows = Math.floor(photoRemain / 3);
let extra = photoRemain % 3;
이게 7장일때, 8장일때는 똑같이 threeRows가 2지만 extra가 각각 1, 2다. 그러니까 6+1, 6+2인건데
// 3n + 1 → 2 + 2 배치
if (extra === 1) {
for (let i = 0; i < 2; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
}
extra가 1일때 저 코드를 돌게 된다. 아니 그럼 애초에 3n+1이 3n+4로 배치되는거니까 걍 4장 남을때까지 3씩 빼면 안되는거임…? 그러면 개적화 되나?
코드_최종.js
const photoContainer = document.querySelector('.photocontainer');
const photoCount = document.querySelector('#photocount');
const photoButton = document.querySelector('#photobutton');
photoButton.addEventListener('click',(e)=>{
let countNum = photoCount.value;
console.log(typeof(countNum));
photoContainer.innerHTML = ''; //아무것도 없지만 일단 비운다
if(isNaN(countNum) || countNum <= 0) {
alert ('유효한 숫자를 입력해주세요!');
};
let photoRemain = countNum;
let photoIndex = 1;
if (photoRemain == 1) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
photoContainer.appendChild(photoInbox);
return;
}
if (photoRemain == 4) {
// 4장은 2 + 2 고정
for (let i = 0; i < 2; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
return;
}
// 여기부터는 일반 배치
let threeRows = 0;
let twoRows = 0;
if (photoRemain % 3 === 1 && photoRemain !== 4) {
// 3n + 1 → 마지막 2 + 2 필요
threeRows = Math.floor((photoRemain - 4) / 3);
twoRows = 2;
} else if (photoRemain % 3 === 2) {
// 3n + 2 → 마지막 2 필요
threeRows = Math.floor((photoRemain - 2) / 3);
twoRows = 1;
} else {
// 3n → 다 3장 줄로 채움
threeRows = Math.floor(photoRemain / 3);
twoRows = 0;
}
// 3장 줄 배치
for (let i = 0; i < threeRows; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 3; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
// 2장 줄 배치
for (let i = 0; i < twoRows; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
});
일단 이것도 바로 나온 게 아니라 문제가 하나 더 있었다. 이번에는 한 장 올렸는데 사진이 4장 올라가서
if (photoRemain === 1) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
photoContainer.appendChild(photoInbox);
return;
}
if (photoRemain === 4) {
// 4장은 2 + 2 고정
for (let i = 0; i < 2; i++) {
const photoInbox = document.createElement('div');
photoInbox.className = 'grid-row';
for (let j = 0; j < 2; j++) {
const photosIndiv = document.createElement('div');
photosIndiv.className = 'photo-row';
photosIndiv.textContent = photoIndex++;
photoInbox.appendChild(photosIndiv);
}
photoContainer.appendChild(photoInbox);
}
return;
}
여기서 ===을 ==로 바꿨다. 그래서 됐어요? 예.
저게 왜 문제냐면 ===로 하면 타입까지 비교하게 되는데, 막 input을 통해 입력받은 숫자는 숫자가 아니라 string… 그니까 문자다. ===를 쓰게 되면 컴퓨터 입장에서는 얘는 문자고 쟤는 숫자니까 다른거지만, ==를 쓰면 어쨌든 1이니까 아 ㅇㅋㅇㅋ 하게 되는 것. 이 사태를 예방하려면 아예 let countNum = photoCount.value;에서 등호 오른쪽을 parseInt로 감싸는 방법도 있다.
.grid-row {
background-color: var(--sub-color);
display: flex;
gap: 10px;
margin-bottom: 10px;
transition: .5s;
}
.photo-row {
flex: 1;
height: 150px;
border-radius: 5px;
background-color: #603F83;
display: flex;
align-items: center;
justify-content: center;
transition: .5s;
color: var(--sub-color);
}
지금까지는 flex를 주는 요소들에 크기 속성이 따로 있었지만, 이번에는 3장일때, 2장일때, 한장일때 크기가 다 다르기때문에 크기가 상황에 따라 변해야 한다. 이걸 근데 JS단에서 만지나요? 아뇨, 그냥 flex: 1;주시면 됩니다.
한장
3n+1(10)
3n+2(14)
3n(9)
4장