CODING/스파르타 내일배움캠프 TIL

11일차_코트카타 Stream_Firebase_Git 특강 복습_방명록 수정기능 구현_24.12.26(목)

codingTrip 2024. 12. 26. 21:33

코트카타

9) 짝수의 합

class Solution {
    public int solution(int n) {
        int answer = 0;
        if(n>0 && n<=1000){
            for(int i=1;i<=n;i++){
                if(i%2==0){
                    answer += i;
                } else {
                    continue;
                }
            }
        }
        return answer;
    }
}

나는 for문, if문과 나머지라는 개념을 활용해서 문제를 해결했다.

 

그런데 다른 분의 풀이를 보니,

문득 IntStream 이라는 개념이 궁금해졌다.

다른 분의 풀이는 아래와 같다.

import java.util.stream.IntStream;
class Solution {
    public int solution(int n) {
        return IntStream.rangeClosed(0, n)
            .filter(e -> e % 2 == 0)
            .sum();
    }
}

 출처 : https://school.programmers.co.kr/learn/courses/30/lessons/120831/solution_groups?language=java

다른 분 : Eisen , cjhfam7539님

 

IntStream이란

  • Java8에서 데이터를 더 효과적으로 처리할 수 있는 새로운 기능인 Stream API 중 한 부분
  • 'int' 에 대한 순차 및 병렬 집계 연산을 수행하는 데 사용
  • 반복문 없이도 배열이나 컬렉션의 데이터를 처리할 수 있음

다른 분이 사용하신 메서드 위주로 살펴보자면

  • rangeClosed' 메소드는 주어진 범위 내 순차적인 정수 스트림을 반환
  • 'range'는 마지막 정수를 포함하지 않는 반면, 'rageClosed'는 마지막 정수를 포함
  • 'filter' :  주어진 술어(predicate)와 일치하는 요소만 포함하는 스트림을 반환
  • 'sum' : 각각 스트림의 요소의 합계 반환

출처 : https://data04190.tistory.com/59

 

익숙하지는 않지만 매우 유용하게 쓰일 것 같은 메소드라서

이런 것이 있다 정도 알아두면 좋을 것 같다.

 

10) 배열의 평균값

class Solution {
    public double solution(int[] numbers) {
        double answer = 0;
        double sum = 0;
        
        if(numbers.length>=1 && numbers.length<=100){
            for(int num:numbers) {
                if(num>=0 && num<=1000){
                    sum += num;
                }
            }
            answer = sum/numbers.length;
        }
        return answer;
    }
}

확장된 for문을 활용했다.

 

다른 분의 풀이를 보니,

Arrays.stream 이라는 개념이 궁금해졌다.

다른 분의 풀이는 아래와 같다.

import java.util.Arrays;

class Solution {
    public double solution(int[] numbers) {
        return Arrays.stream(numbers).average().orElse(0);
    }
}

출처 : https://school.programmers.co.kr/learn/courses/30/lessons/120817/solution_groups?language=java

다른 분 : 홍희표 , 코코몽 , bangjaeyoung , ShimSeongbo 외 96 명

 

배열로 stream을 사용하는 방법
java.util.Arrays의 Arrays.stream() 사용

// stream 객체를 담아서 사용하거나.
Stream<String> stream = Arrays.stream(array);
stream.filter(element -> element.startsWith("e"))
      .forEach(System.out::println);

// 또는 stream 객체를 바로 사용.
Arrays.stream(array).filter(element -> element.startsWith("e"))
      .forEach(System.out::println);

참고로 한번 생성된 스트림 객체는 재활용이 불가능하다. 따라서 후자로 코딩하는 것을 추천한다고 하셨다.

출처 : https://velog.io/@newv/java%EC%97%90%EC%84%9C-%EB%B0%B0%EC%97%B4%EC%9D%84-stream%EC%9C%BC%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0

 

아직은 어렵지만 stream이라는 개념에 대해 친숙해지면 나중에 개발할 때 편할 것 같다.


[왕초보] 코딩이 처음이어도 쉽게 배우는 웹개발 A to Z - 4주차

2, 3주차 복습

https://movie.daum.net/moviedb/main?movieId=68593

"?" 쪼개지는 지점

"?" 기준으로 앞 부분이 <서버 주소> 뒷 부분이 [영화 번호]

 

GET 방식으로 데이터를 전달하는 방법

? : 여기서부터 전달할 데이터가 작성된다는 의미입니다.

& : 전달할 데이터가 더 있다는 뜻입니다.

예시) google.com/search?q=아이폰&sourceid=chrome&ie=UTF-8

google.com의 search 창구에 다음 정보를 전달

q=아이폰 (검색어)

sourceid=chrome (브라우저 정보)

ie=UTF-8 (인코딩 정보)

 

예시를 들자면,

/ 까지는 : 우리은행 용산지점

?은 창구

그 이후는 창구에 가져가는 데이터

 

출처 : 스파르타 내일배움캠프 강의

데이터 베이스의 종류 중 Firebase는 NoSQL이다.

 

파이어베이스에서 데이터를 추가할 때 쓰는 API이다.

$("#id").click(async function () {
    let doc = {};
    await addDoc(collection(db, "콜렉션이름"), doc);
})

 

[왕초보] 코딩이 처음이어도 쉽게 배우는 웹개발 A to Z - 5주차

  •  
// 입력했던 값들을 데이터베이스에 넣기
// 데이터베이스 컬렉션 써서 넣어주기
await addDoc(collection(db, "**빈칸**"), doc)
//데이터들, 입력 폼에서 받기
//이미지(image), 제목(title), 내용(content), 날짜(date)를 하나씩 변수에 담아주기
let doc = { **빈칸** };
//버튼의 아이디를 넣어서 버튼을 클릭하면, 작동되는 함수 만들기
$("**빈칸**").click(async function () { **빈칸** }

 

해당 강의 1회차 수강시에는 async와 await에 대해서 제대로 짚고 넘어가지 않아서 짚어보기로 했다.

async await는 JavaScript에서 비동기 작업을 처리하기 위한 키워드

비동기적인 코드를 보다 간결하고 동기적인 스타일로 작성할 수 있도록 도와줌

 

ex) “손님이 오면 음식을 만든다” 라는 규칙을 정했는데, 손님이 안왔는데 음식부터 나오면 안됨

그래서, 파이어베이스에 접속해서 데이터를 보내기 전에 잠깐 멈춰두기

 

동기 (Synchronous): 동기적인 작업은 순서대로 진행되며, 이전 작업이 완료되어야 다음 작업이 실행됨

비동기 (Asynchronous): 비동기적인 작업은 작업들이 동시에 실행될 수 있으며, 한 작업의 완료를 기다리지 않고 다음 작업을 시작 가능

 

Firebase에서 하나씩 데이터 가져오는 방법

let docs = await getDocs(collection(db, ""));
docs.forEach((doc) => { /*코드 작성*/ });
// 컬렉션 명 설정해 주기
// 이미지, 제목, 별, 추천 이유 값들을 변수에 담기
// 카드 코드 가져와서 변수에 담아주기
// append 메서드를 통해 카드 붙여주기

GIT 기초 특강 복습

1. 필수 리눅스 명령어

Visual studio code Terminal 열기
윈도우는 Bash로 하기

 1) pwd(print working directory)
현재 내가 작업하는 폴더를  보여줌 => 현재 위치

~ Home(홈) : 데스크탑 보다 더 상위의 폴더

 

2) ls(list)

내 폴더 안에 있는 폴더 & 파일 내역

 

3) ls -a(list all)

숨겨진 파일(보통 .으로 시작함)도 모두 보기 가능

 

4) cd 폴더명(change directory)

폴더로 이동 가능(폴더 더블클릭 같은 효과)

cd ..  : 한 단계 위의 폴더

cd 폴더명/폴더명 : 한 번에 더 깊이 들어가기 가능

cd ../.. : 마찬가지로 한 번에 더 위로 가기 가능

 

5) mkdir 폴더명(make directory)

현재 경로에서 폴더를 생성하는 명령어

 

6) touch 파일명

현재 경로에서 파일 생성

(파일의 생성과 날짜와 시간 변경하는 명령어)

 

2. Git & GitHub 개념

Git이란?

버전 관리 도구(형상 관리 도구)

(면접용) 소프트웨어의 변경사항을 체계적으로 추적하고 통제하는 것

 

1) Git : 코드 변경점 기록 (버전 관리 도구)

2) Github : 온라인 백업, 공유, 협업 (온라인 코드 저장소)

 

3. Git 필수 명령어

1. 코드 관리를 시작하는 명령어 - git init

initialize(초기화하다, 초기 세팅하다)

프로젝트 시작 전 딱 한 번만 입력하면 됨

정확한 프로젝트 폴더(경로)에서 입력해야 함(잘못하면 데스크탑 전체 파일, 폴더가 다 기록됨)

 

2. 코드를 저장하는 명령어 - git add & commit

Add 장바구니 - 지정

Commit 구매 - 저장

 

1. git add 파일명

=> 저장하기 전 저장할 파일 지정

2. git commit -m "메세지 작성"

=> 실제로 저장하는 명령어

 

택배 시킬 때 정확하게 주소 입력하는 것처럼

우리 집으로 보내주세요가 아닌

00시 00동 00아파트 00호

이렇게 입력하는 것처럼 주소를 입력해야 한다.

 

git status부터는 이어서 복습하겠다.


팀프로젝트 - 자기소개 페이지 만들기

개인페이지

firebase 데이터 연결해서 불러오기

    <!-- FireBase 연동 및 기능 넣는 구간 -->
    <script type="module">
      /* 파이어베이스 연동부분 제외 */
      
      // 회원 데이터에서 내 아이디에 해당하는 데이터 불러오기
      let docs = await getDoc(doc(db, "members", "codingTrip"));
      let row = docs.data();
      let image = row['profile_url'];
      let name = row['name'];
      let content = row['content'];

      /* html에 위에서 불러온 데이터를 넣어주기 
         이미지 삽입 부분에 css 기능 사용하기 */
      $("#myName").text(name);
      $("#myContent").text(content);
      $('#myImage').css({
        'background-image': `url(${image})`,
        'background-size': 'cover',
        'background-position': 'center',
        'opacity': '0.7',
        
      });
    </script>

 

// 각각의 데이터를 불러와야 하는 곳에 id값을 주었다.
 <div class="showName">    
      <div id="myImage" class="position-relative overflow-hidden p-3 p-md-5 m-md-3 text-center bg-body-tertiary">
      <div class="col-md-6 p-lg-5 mx-auto my-5 myText">
        <h1 id="myName" class="display-3 fw-bold name"></h1>
        <h3 id="myContent"></h3>
      </div>
      <div class="product-device shadow-sm d-none d-md-block"></div>
      <div class="product-device product-device-2 shadow-sm d-none d-md-block"></div>
    </div>
  </div>

 

뒤로가기 버튼

//onclick="location.href='주소'" 사용
</div>
      <button type="button" class="btn btn-info" onclick="location.href='index.html' ">뒤로가기</button>
</div>

 

방명록 삭제 기능, 수정 기능 구현

튜터님께서

로그인 기능이 없으니 방명록 작성시 비밀번호를 두어 수정 또는 삭제를 임의로 없도록 제한해 보면 좋겠습니다

라고 하셔서 이에 대한 기능을 구현해 보았다.

 

해당 기능 구현이 완료되어 수정 기능까지 같이 구현하기로 했다.

   /* 기본적으로 모달창 숨기기 */
      .modal {
        display: none; /* 처음에 보이지 않음 */
        position: fixed;
        z-index: 1000;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow: auto;
        background-color: rgba(0, 0, 0, 0.5);
      }

      /* 모달창 콘텐츠 */
      .modal-content {
        background-color: #fff;
        margin: 15% auto;
        padding: 20px;
        border: 1px solid #888;
        width: 80%;
        max-width: 500px;
        text-align: center;
      }

      /* 닫기 버튼 */
      .close {
        color: #aaa;
        float: right;
        font-size: 28px;
        font-weight: bold;
        cursor: pointer;
      }

      .close:hover,
      .close:focus {
        color: black;
        text-decoration: none;
        cursor: pointer;
      }
      
    //방명록 삭제 이벤트 등록
        $(".gstbook_list").on("click", ".deleteBtn", async function () {
          const guestId = $(this).data("id"); // 버튼 ID에서 guestId 추출
          const pass = $(this).data("password");
          deleteGstBook(guestId, pass);
        });

        //방명록 수정 이벤트 등록
        $(".gstbook_list").on("click", ".updateBtn", async function () {
          const guestId = $(this).data("id"); // 버튼 ID에서 guestId 추출
          const pass = $(this).data("password");
          
          let docs = await getDoc(doc(db, "gst_book", guestId));
          let row = docs.data();
          const text = row["text"];
          updateGstBook(guestId, pass, text);
        });

      //방명록 삭제
      const deleteGstBook = async (docId, pwd) => {
        if (!docId) {
          alert("삭제할 문서를 찾을 수 없습니다.");
          return;
        }
        try {
          // Firestore에서 문서 참조 생성 및 삭제
          let gstBookDocId = doc(db, "gst_book", docId);
          //console.log("삭제할 문서 ID:", gstBookDocId);

          let getpass = prompt("비밀번호를 입력하세요.");

          if (pwd == getpass) {
            await deleteDoc(gstBookDocId);
            alert("방명록이 성공적으로 삭제되었습니다.");
            window.location.reload();
          } else {
            alert("비밀번호가 다릅니다.");
          }
        } catch (error) {
          console.error("방명록 삭제 중 오류 발생:", error);
          alert("방명록 삭제에 실패했습니다.");
        }
      };

      //방명록 수정
      const updateGstBook = async (guestId, pass, text) => {
        if (!guestId) {
          alert("수정할 문서를 찾을 수 없습니다.");
          return;
        }

        let getpass = prompt("비밀번호를 입력하세요.");
        if (pass == getpass) {
          // 수정 버튼 클릭 시 모달창 표시
          $("#modal").fadeIn(); // 부드럽게 표시
          $("#modal-content").val(text);

          // 닫기 버튼 클릭 시 모달창 숨기기
          $(".close").on("click", function () {
            $("#modal").fadeOut(); // 부드럽게 숨김
          });

          // 모달창 외부 클릭 시 모달창 숨기기
          $(window).on("click", function (event) {
            if ($(event.target).is("#modal")) {
              $("#modal").fadeOut(); // 부드럽게 숨김
            }
          });

          //방명록 수정 후 저장 이벤트 등록
          $(".gstbook_list").on("click", ".saveBtn", async function () {
            console.log("guestId", guestId, "저장");
            saveGstBook(guestId, pass);
          });
        }
      };

      //방명록 수정 후 저장
      const saveGstBook = async (guestId, pass) => {
        // 컬렉션과 문서 ID 지정
        const collectionName = "gst_book";
        const documentId = guestId;

        // 수정할 필드와 새 값 지정
        const fieldToUpdate = "text";
        const newValue = $("#modal-content").val();

        // 문서 참조 생성
        let docs = await getDoc(doc(db, "gst_book", guestId));
        const ref = doc(db, collectionName, documentId);
        console.log(ref);

        // 필드 업데이트
        await updateDoc(ref, { text: newValue });

        window.location.reload();
      };

      //방명록 목록 가져오기
      const getAllGstBook = async () => {
        let allDocs = await getDocs(collection(db, "gst_book"));
        let res = [];
        let html = "";

        allDocs.docs.map((doc) => {
          res = [
            ...res,
            {
              id: doc.id,
              ...doc.data(),
            },
          ];
        });

        //생성일 기준 내림차순순
        res.sort((a, b) => b.create_dt - a.create_dt);

        //방명록 리스트 추가
        res.forEach((data) => {
          html =
            html +
            `<div id="${
              data.id
            }" class='pd-3' style='margin-bottom: 15px; border-bottom: 1px solid #ddd; padding-bottom: 10px;'>
          <div style='font-weight: bold; color: #333; margin-bottom: 5px;'>${
            data.username || "익명"
          }</div>
          <div style='color: #555; font-size: 14px; line-height: 1.5;'>${
            data.text
          }</div>
          <div style='font-size: 12px; color: #999; margin-top: 5px;'>${new Date(
            data.create_dt
          ).toLocaleString()}</div>
          <button data-id=${data.id} data-password =${
              data.password
            } type="button" class="btn btn-outline-warning updateBtn">수정</button>
          <button data-id=${data.id} data-password =${
              data.password
            } type="button" class="btn btn-outline-danger deleteBtn">삭제</button>
        </div>
        
    <div id="modal" class="modal">
        <div class="modal-content">
            <span class="close" id="closeButton">&times;</span>
            <input id="modal-content" type="text" style='color: #555; font-size: 14px; line-height: 1.5;'></input>
            <button data-id=${data.id} data-password =${
              data.password
            } type="button" class="btn btn-outline-success saveBtn">저장</button>
        </div>
     </div>
        
        `;
        });
        $(".gstbook_list").empty();
        $(".gstbook_list").append(html);
      };

      /* 방명록 로직 -끝 */

 

        <!-- 방명록 시작-->
        <div class="scroll_action_04 guestBook">
          <div class="mt-2 p-5 bg-gray rounded">
            <h2 class="fs-6">방명록 남기기</h2>
            <div class="guestBook_input_top_wrapper">
              <input
                type="text"
                class="form-control"
                id="gstbook_username"
                style="
                  margin-bottom: 7px;
                  border-radius: 4px;
                  border: 1px solid #ced4da;
                  width: 100px;
                  padding: 6px 12px 6px 12px;
                "
                placeholder="작성자"
              />
              <input
                type="text"
                class="form-control"
                id="gstbook_pw"
                style="
                  margin-bottom: 7px;
                  border-radius: 4px;
                  border: 1px solid #ced4da;
                  width: 100px;
                  padding: 6px 12px 6px 12px;
                "
                placeholder="비밀번호"
              />
            </div>
            <input
              class="form-control"
              type="text"
              style="height: 70px"
              id="gstbook_text"
              placeholder="내용"
            />

            <div class="d-flex mt-2 justify-content-end">
              <input
                style="width: 100px"
                type="submit"
                class="btn btn-primary"
                id="addGstBookBtn"
                te
              />
            </div>
            <div class="gstbook_list"></div>
          </div>
        </div>
        <!-- 방명록 끝-->
      </div>
    </div>

 

 

git pull origin main 하고 제대로 상태 확인 안하고 바로 

git push origin main 하다보니 충돌이 났다...

이럴 경우 차라리 로컬 폴더 삭제 후 다시 git clone을 하는 것이 더 깔끔하다고 팀원분께 배우게 되었다.

다음부터는 pull 후에 잘 확인해야 되겠다.