24.05.17 금 TIL(Today I Learned)

신민금·2024년 5월 17일
0
post-thumbnail

TIL(Today I Learned)

: 매일 저녁, 하루를 마무리하며 작성 !
: ⭕ 지식 위주, 학습한 것을 노트 정리한다고 생각하고 작성하면서 머리 속 흩어져있는 지식들을 정리 !


알고리즘 코드카타

  • 문제 설명
    어떤 정수들이 있습니다. 이 정수들의 절댓값을 차례대로 담은 정수 배열 absolutes와 이 정수들의 부호를 차례대로 담은 불리언 배열 signs가 매개변수로 주어집니다. 실제 정수들의 합을 구하여 return 하도록 solution 함수를 완성해주세요.
  • 제한사항
    absolutes의 길이는 1 이상 1,000 이하입니다.
    absolutes의 모든 수는 각각 1 이상 1,000 이하입니다.
    signs의 길이는 absolutes의 길이와 같습니다.
    signs[i] 가 참이면 absolutes[i] 의 실제 정수가 양수임을, 그렇지 않으면 음수임을 의미합니다.
class Solution {
    public int solution(int[] absolutes, boolean[] signs) {
        int answer = 0;
        for(int i=0; i<absolutes.length; i++){
            if(signs[i]){
            answer = answer + absolutes[i];
                }else if(!signs[i]){
                answer = answer - absolutes[i];
            }
        } 
        return answer;
    }
}

개인 프로젝트 제출 완료

6단계까지 구현

// SchedulerController.java

package com.sparta.scheduler.controller;

import com.sparta.scheduler.dto.SchedulerRequestDto;
import com.sparta.scheduler.dto.SchedulerResponseDto;
import com.sparta.scheduler.entity.Scheduler;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Date;
import java.util.List;

@RestController
@RequestMapping("/api")
public class SchedulerController {

    private final JdbcTemplate jdbcTemplate;

    public SchedulerController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @PostMapping("/schedulers")
    public SchedulerResponseDto createScheduler(@RequestBody SchedulerRequestDto requestDto) {
        // RequestDto -> Entity
        Scheduler scheduler = new Scheduler(requestDto);

        // DB 저장
        KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체

        String sql = "INSERT INTO scheduler (title, contents, username, password, date) VALUES (?, ?, ?, ?, ?)";
        jdbcTemplate.update(con -> {
                    PreparedStatement preparedStatement = con.prepareStatement(sql,
                            Statement.RETURN_GENERATED_KEYS);

                    preparedStatement.setString(1, scheduler.getTitle());
                    preparedStatement.setString(2, scheduler.getContents());
                    preparedStatement.setString(3, scheduler.getUsername());
                    preparedStatement.setString(4, scheduler.getPassword());
                    preparedStatement.setDate(5, scheduler.getDate());

                    return preparedStatement;
                },
                keyHolder);

        // DB Insert 후 받아온 기본키 확인
        Long id = keyHolder.getKey().longValue();
        scheduler.setId(id);

        // Entity -> ResponseDto
        SchedulerResponseDto schedulerResponseDto = new SchedulerResponseDto(scheduler);

        return schedulerResponseDto;
    }

    @GetMapping("/schedulers")
    public List<SchedulerResponseDto> getschedulers() {
        // DB 조회
        String sql = "SELECT * FROM scheduler";

        return jdbcTemplate.query(sql, new RowMapper<SchedulerResponseDto>() {
            @Override
            public SchedulerResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                // SQL 의 결과로 받아온 Schedulers 데이터들을 SchedulersResponseDto 타입으로 변환해줄 메서드
                Long id = rs.getLong("id");
                String title = rs.getString("title");
                String contents = rs.getString("contents");
                String username = rs.getString("username");
                String password = rs.getString("password");
                Date date = rs.getDate("date");
                return new SchedulerResponseDto(id, title, contents, username, password, date);
            }
        });
    }

    @PutMapping("/schedulers/{id}")
    public Long updateScheduler(@PathVariable Long id, @RequestBody SchedulerRequestDto requestDto) {
        // 해당 메모가 DB에 존재하는지 확인
        Scheduler scheduler = findById(id);
        if (scheduler != null) {
            // 비밀번호 확인
            if (!scheduler.getPassword().equals(requestDto.getPassword())) {
                throw new ResponseStatusException(HttpStatus.FORBIDDEN, "비밀번호가 올바르지 않습니다.");
            }

            // Null 및 빈 값 검사 추가
            validateRequestDto(requestDto);

            // scheduler 내용 수정
            String sql = "UPDATE scheduler SET title = ?, contents = ?, username = ?, password = ?, date = ? WHERE id = ?";

            jdbcTemplate.update(sql, requestDto.getTitle(), requestDto.getContents(),
                    requestDto.getUsername(), requestDto.getPassword(), requestDto.getDate(), id);

            return id;
        } else {
            throw new IllegalArgumentException("선택한 일정은 존재하지 않습니다.");
        }
    }

    private void validateRequestDto(SchedulerRequestDto requestDto) {
        if (requestDto.getTitle() == null || requestDto.getTitle().isEmpty()) {
            throw new IllegalArgumentException("Title cannot be null or empty");
        }
        if (requestDto.getContents() == null || requestDto.getContents().isEmpty()) {
            throw new IllegalArgumentException("Contents cannot be null or empty");
        }
        if (requestDto.getUsername() == null || requestDto.getUsername().isEmpty()) {
            throw new IllegalArgumentException("Username cannot be null or empty");
        }
        if (requestDto.getPassword() == null || requestDto.getPassword().isEmpty()) {
            throw new IllegalArgumentException("Password cannot be null or empty");
        }
        if (requestDto.getDate() == null) {
            throw new IllegalArgumentException("Date cannot be null");
        }
    }

    @DeleteMapping("/schedulers/{id}")
    public Long deleteScheduler(@PathVariable Long id, @RequestBody SchedulerRequestDto requestDto) {
        // 해당 메모가 DB에 존재하는지 확인
        Scheduler scheduler = findById(id);
        if (scheduler != null) {
            // 비밀번호 확인
            if (!scheduler.getPassword().equals(requestDto.getPassword())) {
                throw new ResponseStatusException(HttpStatus.FORBIDDEN, "비밀번호가 올바르지 않습니다.");
            }

            // scheduler 삭제
            String sql = "DELETE FROM scheduler WHERE id = ?";
            jdbcTemplate.update(sql, id);

            return id;
        } else {
            throw new IllegalArgumentException("선택한 일정은 존재하지 않습니다.");
        }
    }

    private Scheduler findById(Long id) {
        // DB 조회
        String sql = "SELECT * FROM scheduler WHERE id = ?";

        return jdbcTemplate.query(sql, resultSet -> {
            if (resultSet.next()) {
                Scheduler scheduler = new Scheduler();
                scheduler.setTitle(resultSet.getString("title"));
                scheduler.setContents(resultSet.getString("contents"));
                scheduler.setUsername(resultSet.getString("username"));
                scheduler.setPassword(resultSet.getString("password"));
                scheduler.setDate(resultSet.getDate("date"));
                return scheduler;
            } else {
                return null;
            }
        }, id);
    }
}


// SchedulerRequestDto.java ------------------------------------------

package com.sparta.scheduler.dto;

import lombok.Getter;

import java.sql.Date;

@Getter
public class SchedulerRequestDto {
    private String title;
    private String contents;
    private String username;
    private String password;
    private Date date;
}


// SchedulerResponseDto.java ------------------------------------------


package com.sparta.scheduler.dto;

import com.sparta.scheduler.entity.Scheduler;
import lombok.Getter;

import java.sql.Date;

@Getter
public class SchedulerResponseDto {
    private Long id;
    private String title;
    private String contents;
    private String username;
    private String password;
    private Date date;

    public SchedulerResponseDto(Scheduler scheduler) {
        this.id = scheduler.getId();
        this.title = scheduler.getTitle();
        this.contents = scheduler.getContents();
        this.username = scheduler.getUsername();
        this.password = scheduler.getPassword();
        this.date = scheduler.getDate();
    }

    public SchedulerResponseDto(Long id, String title, String contents, String username, String password, Date date ) {
        this.id = id;
        this.title = title;
        this.contents = contents;
        this.username = username;
        this.password = password;
        this.date = date;


    }
}


// Scheduler.java ------------------------------------------

package com.sparta.scheduler.entity;

import com.sparta.scheduler.dto.SchedulerRequestDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.sql.Date;

@Getter
@Setter
@NoArgsConstructor
public class Scheduler {
    private Long id;
    private String title;
    private String contents;
    private String username;
    private String password;
    private Date date;

    public Scheduler(SchedulerRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.contents = requestDto.getContents();
        this.username = requestDto.getUsername();
        this.password = requestDto.getPassword();
        this.date = requestDto.getDate();



    }

    public void update(SchedulerRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.contents = requestDto.getContents();
        this.username = requestDto.getUsername();
        this.password = requestDto.getPassword();
        this.date = requestDto.getDate();

    }
}


// index.html ------------------------------------------

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>일정 관리 서비스</title>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&display=swap" rel="stylesheet">

    <style>
        @import url(//spoqa.github.io/spoqa-han-sans/css/SpoqaHanSans-kr.css);
        body {
            margin: 0px;
        }
        .area-edit {
            display: none;
        }
        .wrap {
            width: 538px;
            margin: 10px auto;
        }
        .area-write {
            position: relative;
            width: 538px;
            padding: 20px;
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }
        .area-write img {
            cursor: pointer;
            position: absolute;
            width: 22.2px;
            height: 18.7px;
            bottom: 20px;
            right: 20px;
        }
        .background-header {
            position: fixed;
            z-index: -1;
            top: 0px;
            width: 100%;
            height: 428px;
            background-color: #339af0;
        }
        .background-body {
            position: fixed;
            z-index: -1;
            top: 428px;
            height: 100%;
            width: 100%;
            background-color: #dee2e6;
        }
        .header {
            margin-top: 50px;
        }
        .header h2 {
            height: 33px;
            font-size: 42px;
            font-weight: 500;
            text-align: center;
            color: #ffffff;
        }
        .header p {
            margin: 40px auto;
            width: 217px;
            height: 48px;
            font-family: 'Noto Sans KR', sans-serif;
            font-size: 16px;
            font-weight: 500;
            text-align: center;
            color: #ffffff;
        }
        .field, .field-title, .field-username, .field-password, .field-date {
            width: 100%;
            border-radius: 5px;
            background-color: #ffffff;
            border: 1px solid #ced4da;
            padding: 18px;
            resize: none;
            margin-bottom: 10px;
            box-sizing: border-box;
        }
        textarea.field::placeholder, .field-title::placeholder, .field-username::placeholder, .field-password::placeholder, .field-date::placeholder {
            font-family: 'Noto Sans KR', sans-serif;
            font-size: 16px;
            font-weight: normal;
            text-align: left;
            color: #868e96;
        }
        .card {
            width: 538px;
            border-radius: 5px;
            background-color: #ffffff;
            margin-bottom: 12px;
        }
        .card .metadata {
            position: relative;
            display: flex;
            font-family: 'Spoqa Han Sans';
            font-size: 11px;
            font-weight: normal;
            text-align: left;
            color: #adb5bd;
            height: 14px;
            padding: 10px 23px;
        }
        .card .metadata .date {
        }
        .card .metadata .username {
            margin-left: 20px;
        }
        .title, .contents {
            padding: 0px 23px;
        }
        .title .text, .contents .text {
            word-wrap: break-word;
            word-break: break-all;
        }
        .edit {
            display: none;
        }
        .te-edit {
            border-right: none;
            border-top: none;
            border-left: none;
            resize: none;
            border-bottom: 1px solid #212529;
            width: 100%;
            font-family: 'Spoqa Han Sans';
        }
        .footer {
            position: relative;
            height: 40px;
        }
        .footer img.icon-start-edit {
            cursor: pointer;
            position: absolute;
            bottom: 14px;
            right: 55px;
            width: 18px;
            height: 18px;
        }
        .footer img.icon-end-edit {
            cursor: pointer;
            position: absolute;
            display: none;
            bottom: 14px;
            right: 55px;
            width: 20px;
            height: 15px;
        }
        .footer img.icon-delete {
            cursor: pointer;
            position: absolute;
            bottom: 12px;
            right: 19px;
            width: 14px;
            height: 18px;
        }
        #cards-box {
            margin-top: 12px;
        }
    </style>
    <script>
        // 사용자가 내용을 올바르게 입력하였는지 확인합니다.
        function isValidContents(contents) {
            if (contents == '') {
                alert('내용을 입력해주세요');
                return false;
            }
            if (contents.trim().length > 500) {
                alert('공백 포함 500자 이하로 입력해주세요');
                return false;
            }
            return true;
        }

        // 수정 버튼을 눌렀을 때, 기존 작성 내용을 textarea 에 전달합니다.
        // 숨길 버튼을 숨기고, 나타낼 버튼을 나타냅니다.
        function editPost(id) {
            showEdits(id);
            let title = $(`#${id}-title`).text().trim();
            let contents = $(`#${id}-content`).text().trim();
            let username = $(`#${id}-username`).text().trim();
            let date = $(`#${id}-date`).text().trim();
            $(`#${id}-title-textarea`).val(title);
            $(`#${id}-content-textarea`).val(contents);
            $(`#${id}-username-textarea`).val(username);
            $(`#${id}-date-textarea`).val(date);
        }

        function showEdits(id) {
            // title, content, username, date 텍스트 에어리어만 표시
            $(`#${id}-editarea-title`).show();
            $(`#${id}-editarea-content`).show();
            $(`#${id}-editarea-username`).show();
            $(`#${id}-editarea-date`).show();
            $(`#${id}-editarea-password`).show(); // 비밀번호 입력 필드 표시
            $(`#${id}-submit`).show();
            $(`#${id}-delete`).show();

            // 기존 텍스트는 숨김
            $(`#${id}-title`).hide();
            $(`#${id}-content`).hide();
            $(`#${id}-username`).hide();
            $(`#${id}-date`).hide();
            $(`#${id}-edit`).hide();

            // 이전 값 유지
            let prevTitle = $(`#${id}-title`).text().trim();
            let prevContents = $(`#${id}-content`).text().trim();
            let prevUsername = $(`#${id}-username`).text().trim();
            let prevDate = $(`#${id}-date`).text().trim();
            $(`#${id}-title-textarea`).val(prevTitle);
            $(`#${id}-content-textarea`).val(prevContents);
            $(`#${id}-username-textarea`).val(prevUsername);
            $(`#${id}-date-textarea`).val(prevDate);
        }

        $(document).ready(function() {
            // HTML 문서를 로드할 때마다 실행합니다.
            getMessages();

            // 수정 버튼 클릭 시 이벤트 설정
            $(document).on("click", ".icon-start-edit", function() {
                let id = $(this).attr("id").split("-")[0];
                showEdits(id);
            });
        });
        // 일정을 불러와서 보여줍니다.
        function getMessages() {
            // 1. 기존 메모 내용을 지웁니다.
            $('#cards-box').empty();
            // 2. 일정 목록을 불러와서 HTML로 붙입니다.
            $.ajax({
                type: 'GET',
                url: '/api/schedulers',
                success: function (response) {
                    for (let i = 0; i < response.length; i++) {
                        let message = response[i];
                        let id = message['id'];
                        let title = message['title'];
                        let username = message['username'];
                        let contents = message['contents'];
                        let date = message['date'];
                        addHTML(id, title, username, contents, date);
                    }
                }
            })
        }

        // 메모 하나를 HTML로 만들어서 body 태그 내 원하는 곳에 붙입니다.
        function addHTML(id, title, username, contents, date) {
            // 1. HTML 태그를 만듭니다.
            let tempHtml = `<div class="card">
    <!-- date/username 영역 -->
    <div class="metadata">
        <div id="${id}-date" class="date">
            ${date}
        </div>
        <div id="${id}-username" class="username">
            ${username}
        </div>
    </div>
    <!-- title 영역 -->
    <div class="title">
        <div id="${id}-title" class="text">
            ${title}
        </div>
        <div id="${id}-editarea-title" class="edit">
            <textarea id="${id}-title-textarea" class="te-edit" name="" cols="30" rows="1"></textarea>
        </div>
    </div>
    <!-- contents 조회/수정 영역-->
    <div class="contents">
        <div id="${id}-content" class="text">
            ${contents}
        </div>
        <div id="${id}-editarea-content" class="edit">
            <textarea id="${id}-content-textarea" class="te-edit" name="" cols="30" rows="5"></textarea>
        </div>
        <div id="${id}-editarea-username" class="edit">
            <textarea id="${id}-username-textarea" class="te-edit" name="" cols="30" rows="1"></textarea>
        </div>
        <div id="${id}-editarea-date" class="edit">
            <input type="date" id="${id}-date-textarea" class="field-date te-edit">
        </div>
        <div id="${id}-editarea-password" class="edit">
            <input type="password" id="${id}-password-textarea" class="te-edit" placeholder="비밀번호를 입력해주세요">
        </div>
    </div>
    <!-- 버튼 영역-->
    <div class="footer">
        <img id="${id}-edit" class="icon-start-edit" src="images/edit.png" alt="수정"token interpolation">${id}')">
        <img id="${id}-delete" class="icon-delete" src="images/delete.png" alt="삭제"token interpolation">${id}')">
        <img id="${id}-submit" class="icon-end-edit" src="images/done.png" alt="완료"token interpolation">${id}')">
    </div>
</div>`;
            // 2. #cards-box 에 HTML을 붙인다.
            $('#cards-box').append(tempHtml);
        }

        // 일정을 생성합니다.
        function writePost() {
            // 1. 입력된 값을 읽어옵니다.
            let title = $('#title').val();
            let contents = $('#contents-textarea').val();
            let username = $('#username').val();
            let password = $('#password').val();
            let date = $('#date').val(); // 사용자가 선택한 날짜 값 읽기

            // 2. 유효성 검사를 진행합니다.
            if (!isValidContents(contents)) {
                return;
            }

            // 3. 일정을 생성합니다.
            let data = {title: title, contents: contents, username: username, password: password, date: date}; // date 포함
            $.ajax({
                type: 'POST',
                url: '/api/schedulers',
                contentType: 'application/json',
                data: JSON.stringify(data),
                success: function (response) {
                    // 4. 성공하면, 일정을 목록에 추가하고 입력 필드를 초기화합니다.
                    addHTML(response.id, title, username, contents, date); // date 포함
                    $('#title').val('');
                    $('#contents-textarea').val('');
                    $('#username').val('');
                    $('#password').val('');
                    $('#date').val(''); // 입력 필드 초기화
                }
            });
        }

        // 일정을 삭제합니다.
        function deleteOne(id) {
            $.ajax({
                type: 'DELETE',
                url: `/api/schedulers/${id}`,
                success: function (response) {
                    alert('메모를 삭제했습니다.');
                    getMessages();
                }
            })
        }

        // 수정을 반영합니다.
        function submitEdit(id) {
            let title = $(`#${id}-title-textarea`).val().trim();
            let contents = $(`#${id}-content-textarea`).val().trim();
            let username = $(`#${id}-username-textarea`).val().trim();
            let date = $(`#${id}-date-textarea`).val().trim();
            let password = $(`#${id}-password-textarea`).val().trim();

            if (!isValidContents(contents)) {
                return;
            }

            let data = {title, contents, username, date, password};

            $.ajax({
                type: "PUT",
                url: `/api/schedulers/${id}`,
                contentType: "application/json",
                data: JSON.stringify(data),
                success: function(response) {
                    alert('일정이 수정되었습니다.');
                    getMessages();
                },
                error: function(response) {
                    if (response.status === 403) {
                        alert('비밀번호가 올바르지 않습니다. 다시 시도해주세요.');
                    }
                }
            });
        }

        function deleteOne(id) {
            let password = prompt("비밀번호를 입력해주세요.");
            if (password == null) {
                return; // 사용자가 취소를 누른 경우
            }

            let data = {password};

            $.ajax({
                type: "DELETE",
                url: `/api/schedulers/${id}`,
                contentType: "application/json",
                data: JSON.stringify(data),
                success: function(response) {
                    alert('일정이 삭제되었습니다.');
                    getMessages();
                },
                error: function(response) {
                    if (response.status === 403) {
                        alert('비밀번호가 올바르지 않습니다. 다시 시도해주세요.');
                    }
                }
            });
        }

    </script>
</head>
<body>
<div class="wrap">
    <!-- header 영역-->
    <div class="header">
        <h2>나만의 일정 관리 서비스</h2>
        <p>스케줄을 작성하고 관리하세요</p>
    </div>
    <!-- write 영역 -->
    <div class="area-write">
        <input type="text" id="title" class="field-title" placeholder="제목을 입력해주세요">
        <textarea id="contents-textarea" class="field" placeholder="내용을 입력해주세요"></textarea>
        <input type="text" id="username" class="field-username" placeholder="작성자를 입력해주세요">
        <input type="password" id="password" class="field-password" placeholder="비밀번호를 입력해주세요">
        <input type="date" id="date" class="field-date"> <!-- 날짜 선택 입력 칸 추가 -->
        <img src="images/send.png" alt="작성" onclick="writePost()">
    </div>

    <!-- card 영역 -->
    <div id="cards-box"></div>
</div>
<div class="background-header"></div>
<div class="background-body"></div>
</body>
</html>

profile
야옹

0개의 댓글