반응형

개발자로 직종을 변경하고 친구들에게 개발자에게 필요한 자격증에 대해 얘기하다가 정보처리기사 자격증을 알게 되었다.

그래서 회사를 퇴사하자마자 바로 2023년 3회차 시험에 필기시험을 접수하고 본격적인 준비를 시작했다.

 

정보처리기사 필기 합격기준

 
총 5과목으로 구성되어 있으며 한 과목당 40점은 넘어야 하고, 평균 60점 이상이여야 합격할 수 있다.
 
코로나가 끝난 후 개발자로의 취업은 많이 어려워졌다는 것을 많이 들었다.
 
그래서 비전공자인 나는 이 정보처리기사 자격증이 꼭 필요했다.
 
그래서 6월에 회사를 퇴사하자마자 필기 준비를 시작했다.
 

정보처리기사 필기 공부법

 
정보처리기사 필기는 CBT 형식으로 치뤄지고, 시험장에서 PC로 시험을 치고 합/불합격을 바로 확인할 수 있다.
 
짧은 시간 내에 합격하기 위해 나는 기출문제 사이트인 CBT 사이트를 이용해
 
매일 4~5시간동안 필기 기출을 반복적으로 공부하였다.
 
https://www.comcbt.com/

 

최강 자격증 기출문제 전자문제집 CBT

전자문제집, CBT, 컴씨비티, 씨비티, 기사, 산업기사, 기능사, 컴활, 컴퓨터활용능력, 1급, 2급, 워드, 정보처리, 전기, 소방, 기계, 사무자동화, 정보기기, 제과, 제빵, 한국사, 공무원, 수능, 필기,

www.comcbt.com

 
여기 사이트에서 기출문제를 반복한 결과 1트만에 합격!
 
필기 합격 후 실기가 남아있어 실기를 바로 준비하였다.
 
하지만....
 
 

정보처리기사 실기 합격기준 

 
정보처리기사는 필답형으로 진행되며 20문제가 출제된다고 한다.
 
프로그래밍 문제와 단답형과 계산형 문제가 나오는데 기준은 아래와 같다.
 
실기 : 100점을 만점으로 하여 60점 이상.
 
이 23년 3회 실기시험을 신청했는데 이 때 학원 교육을 듣고 프로젝트를 진행하면서 실기 시험 할 시간이 많이 없었다.
 
그래도 응시료가 아까워 실기는 어떻게 나오는지 맛보러 가보자는 마음으로 실기 시험을 응시했는데 그 결과..
 

불합격 컷

 
그래도 실기가 어떻게 나오는지 경험해봤다는 생각으로 다음 회차에는 꼭 합격하겠다는 생각으로 24년 1회차 실기 시험을 위해 실기 공부를 2배 이상 공부했다.
 

정보처리기사 실기 공부법

 
정보처리기사 실기는 필기와는 다르게 기출문제만 봐서는 합격하기 힘든 시험이다.
 
그래서 나는 실기 이론과 기출+예상문제가 있는 수제비 정보처리기사 실기 책을 구입했다.
 
그리고 학원 친구들과 매주 스터디도 하면서 자격증 취득을 위해 열심히 공부를 했다.
 

 
비전공자를 위한 최고의 수험서라고 책에 적혀있는데 비전공자가 이해하기에 쉽기도 했고, 수제비 카페가 있어 스터디도 할 수 있고 유튜브도 있어 많은 정보를 얻을 수 있었다.
 
그리고 프로그래밍 문제는 흥달쌤 유튜브를 많이 참고했다. 특히나 C언어 부분이 비전공자인 나는 많이 어려웠는데 유튜브를 보면서 많은 도움이 되었다.
https://www.youtube.com/@HeungSsaem

 

흥달쌤

흥달쌤과 함께하는 IT 채널입니다. 정보처리기사 자격증 관련된 강의 및 실무 노하우, 프로그래밍 언어(JAVA, C언어, Python) 특강 등이 진행됩니다. 앞으로 진행 예정인 동영상은 IT 관련된 이야기

www.youtube.com

 
특히 실기 시험 전 날 라이브 강의도 해주셨는데 유튜브로 라이브 강의도 보면서 시험에 잘나올법한 내용들을 한번도 복습했던게 컸었다.
 
 

대망의 실기 시험날

 
정보처리기사 실기는 부산 동의대학교에서 시험을 쳤다.
 
시험 난이도는 작년 3회차 시험 보다 난이도가 쉬웠다고 생각했다. 내가 예상한 문제도 나온거 같았고, 프로그래밍 언어 문제도 무난하게 나온것으로 기억한다.
 
실기 시험을 치고 개발자로 취업하기 위해 회사 면접도 보면서 서울에 있는 개발 회사에 취업하고 몇일 뒤 실기 결과가 나왔다!
 

실기 시험은 2트만에 성공!

 
뭔가 실기시험 끝나고 합격할거 같다는 느낌반 몇 점차이로 떨어질거 같다는 생각 반으로 반신반의했는데 운이 좋게도 합격을 했다. 실기시험은 약간의 문제 운도 있는거 같다.
 

개발자의 첫 자격증

 
드디어 나도 정처기를 손에 넣었다. 이제 리눅스 마스터랑 SQLD 자격증도 도전 할 예정이다.

반응형

'자격증 > 정보처리기사' 카테고리의 다른 글

SW 구축 _ 프로젝트 계획 및 분석  (0) 2023.09.19
SW 구축 _ 소프트웨어 공학  (0) 2023.09.19
반응형

자바스크립트를 사용하여 프로그래밍을 할 때는
데이터를 변수와 상수라는 박스를 만들어 그 안에 넣어서 사용합니다.
이때, 변하지 않는 값이라면 변수를, 고정된 값이라면 상수를 사용합니다.

 

변수와 상수?

 

어떤 의미를 가진 하나의 데이터가 여러 곳에서 사용될 때 변수나 상수를 사용하면 코드 작성이 간편해집니다.

이때 값을 저장하고 변경할 수 있는 것을 변수, 똑같이 값을 저장하지만 변하지 않는 고정값을 담는 것을 상수라고 합니다.

 

다음은 변수와 상수를 선언한 코드입니다. 해석을 해보자면 switchOn 이라는 박스에 true 라는 값이, PI(원주율)이라는 박스에 3.1415926535라는 값이 들어있다는 뜻입니다. 여기서 switchOn은 let을 사용한 변수, PI는 const를 사용한 상수라고 부릅니다.

이처럼 변수와 상수는 이름을 통해 어떤 데이터에 의미를 부여하는 역할도 할 수 있습니다.

let switchOn = true;
const PI = 3.1415926535

 

변수를 사용하면 수정 내용을 코드 전체에 동일하게 적용해야 할 때 변수만 바꿔 주면 되어 무척 편리합니다.

switchOn 의 true를 false로 바꾸면 switchOn을 사용한 코드에 들어가는 모든 값이 바뀌게 됩니다. 반면 고정된 값을 여러 곳에서 사용할 때는 상수를 사용합니다.

 

 

변수를 사용할 때 var를 사용하지 않나요?

ES6 이전에는 자바스크립트에서 변수를 선언할 때 var라는 명령어를 사용하였습니다. 자바스크립트 초창기에는 이것이 굉장히 불안정해 오류의 원인으로 작용했습니다. 하지만 오늘날 ES6 버전 이후부터는 let과 const가 var를 대체합니다.

*
var x = 1;
var y = 2;

console.log(x, y);

* 이렇게 코드를 작성하면 같은 이름의 변수를 두 번 선언할 수 있는 등 코드를 불안정하게 하는 원인이 됩니다.

 

간단히 말해서 let은 변수를 만드는 키워드, const는 상수를 만드는 키워드라 생각하시면 됩니다.

키워드 의미 특징
let 변수를 선언 넣은 데이터를 변경할 수 있음
const 상수를 선언 넣은 데이터를 변경할 수 없음

 

let

 

아래의 코드는 a라는 박스에 1이라는 데이터를 담는다는 뜻입니다. 프로그래밍에서 등호(=)는 단순히 같다는 뜻이 아니라 왼쪽에 오른쪽의 값을 넣는다고 이해해야 합니다. 따라서 콘솔로 a를 출력하면 let 변수 선언을 통해 담긴 1이라는 결과가 나옵니다.

 

 

b라는 박스를 하나더 만들어서 b에 a값을 넣어보겠습니다.

a, b 박스의 값이 각각 1로 출력되었습니다.

오른쪽에 있는 a라는 박스의 값을 왼쪽의 b 박스에 넣어줬다고 생각하시면 됩니다.

 

 

이번에는 let을 붙이지 않고 a 박스에 2를 넣어보겠습니다. 왜냐하면 a라는 박스가 이미 만들어져있기 때문입니다. 그러면 a 박스에 원래 들어가 있던 값이 2로 변경되었기 때문에 출력결과도 2로 나오는 것을 볼 수 있습니다.

 

 

이번에는 a에는 "주녘의 IT기술 블로그" 라는 문자열을 b에는 true라는 불리언(참/거짓) 값을 넣어보겠습니다. 출력 결과를 보면 a, b에 들어있는 값이 성공적으로 바뀐 것을 확인할 수 있습니다. let을 붙이지 않았기 때문에 새로운 박스를 만드는 것이 아니라 박스안에 있는 값만 바뀌었습니다.

 

 

const

 

상수는 const라는 키워드를 사용하여 다음과 같이 선언합니다. A라는 박스에 상수 1을 넣고 출력하면 1이라는 결과가 나옵니다.

* 상수는 일반적으로 프로그래밍에서는 대문자로 이름을 짓습니다.

 

여기에 다른 값을 넣으려고 하면 오류가 발생해 코드가 실행되지 않습니다. 따라서 어떤 데이터가 절대 바뀌면 안되는 값을 가져야 한다면 const 키워드를 이용해 상수를 선언하면 됩니다.

식별자 명명 규칙
  • 영문, 한글 및 유니코드(대부분의 문자 표현) 글자, 숫자 사용 가능
  • 특수문자는 & 또는 _ 사용가능
  • 숫자로 시작할 수 없음
  • 공백(스페이스) 사용 불가
  • 예약어 사용불가
반응형

'JavaScript > JS 이론' 카테고리의 다른 글

템플릿 문자열  (0) 2023.08.31
[JS] 기본이론  (0) 2023.08.23
[JS] 문서 객체 모델 (DOM)  (0) 2023.08.22
[JavaScript] 자바스크립트의 기본 입출력방식  (0) 2023.08.16
반응형

서버에 요청을 보낼 때는 주소를 통해 요청의 내용을 표현합니다.

주소가 /index.html 이면 서버의 index.html을 보내달라는 뜻이고, /about.html 이면 about.html을 보내달라는 뜻입니다.

 

그리고 항상 html 만 요청할 필요는 없습니다. css나 js 그리고 이미지 같은 파일을 요청할 수도 있고, 특정 동작을 행하는 것을 요청할 수 도 있습니다. 요청의 내용이 주소를 통해 표현되므로 서버가 이해하기 쉬운 주소를 사용하는 것이 좋습니다. 여기서 REST라는 개념이 등장합니다.

 

REST(Representational State Transfer)는 웹 기반 시스템에서 자원을 정의하고, 그 자원에 대한 상태를 주고 받는 방법을 기술한 아키텍처 스타일입니다. REST는 클라이언트와 서버 간의 통신을 위한 규칙을 제공하여 분산 시스템을 구축하는 데 사용됩니다.

  1. 자원(Resource) 지향: 모든 자원에는 고유한 식별자(URI) 있으며, 클라이언트는 이를 통해 자원을 식별하고 조작합니다. 예를 들어, 페이지의 URI 해당 자원의 위치를 나타내며 클라이언트는 이를 통해 해당 페이지를 요청할 있습니다.
  2. 상태를 주고받는 구조(Stateful): REST 상태를 저장하지 않고, 요청 간의 상태 전환을 통해 데이터를 주고 받습니다. 이는 서버가 클라이언트의 상태를 관리하지 않고, 요청이 독립적으로 처리되도록 합니다.
  3. 표준화된 인터페이스: REST 자원에 대한 표준화된 인터페이스를 제공합니다. 주요 HTTP 메서드(GET, POST, PUT, DELETE) 사용하여 자원을 조작하고, HTTP 상태 코드를 통해 요청의 성공 또는 실패를 알려줍니다.
  4. 계층화(Layered): REST 아키텍처는 서버와 클라이언트 사이에 중간 계층을 두어 구조를 단순화하고 확장성을 향상시킵니다. 이는 서버와 클라이언트 간의 결합도를 줄이고, 시스템의 확장성과 보안성을 향상시킵니다.

REST에서는 주소 외에도 HTTP 요청 메서드라는 것을 사용합니다.

REST에서 사용되는 주요 HTTP 메서드는 다음과 같습니다.

  1. GET: 리소스의 조회를 요청할 때 사용됩니다. 서버로부터 데이터를 가져오는 요청에 사용되며, 요청한 URI의 정보를 반환합니다. GET 요청은 클라이언트가 서버에게 어떤 리소스의 상태를 조회하기 위해 사용됩니다.
  2. POST: 리소스를 생성하기 위해 사용됩니다. 클라이언트가 서버에 새로운 데이터를 제출할 때 사용되며, 서버는 해당 데이터를 처리하고 새로운 리소스를 생성합니다. 예를 들어, 사용자가 폼을 제출하여 새로운 블로그 게시물을 작성하는 경우 POST 요청을 사용할 수 있습니다.
  3. PUT: 리소스의 전체적인 업데이트를 요청할 때 사용됩니다. 클라이언트가 서버에게 리소스의 전체적인 업데이트를 요청하고자 할 때 사용됩니다. 즉, 클라이언트가 요청한 리소스의 모든 내용을 제공하여 서버에 새로운 내용으로 업데이트합니다.
  4. DELETE: 리소스의 삭제를 요청할 때 사용됩니다. 클라이언트가 서버에게 특정 리소스를 삭제하도록 요청하는 메서드입니다. 서버는 해당 리소스를 삭제하고 삭제에 성공했는지에 대한 상태 코드를 반환합니다.
  5. PATCH: 리소스의 일부를 업데이트하기 위해 사용됩니다. PUT과 유사하지만, PATCH는 리소스의 일부분만을 업데이트할 때 사용됩니다. 즉, 클라이언트가 리소스의 일부만을 제공하여 해당 부분을 업데이트하도록 요청합니다.

 

💡 실전예제

프로젝트를 아래와 같이 구성하고 각 파일마다 코드를 아래와 같이 작성합니다.

📘 restFront.css

* CSS 부분과 HTML 부분은 중요하지 않으므로 복사해서 사용하는 것을 권장합니다.

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
  }
  
  nav {
    background-color: #333;
    color: #fff;
    padding: 10px;
  }
  
  nav a {
    color: #fff;
    text-decoration: none;
    margin-right: 10px;
  }
  
  nav a:hover {
    text-decoration: underline;
  }
  
  div {
    max-width: 600px;
    margin: 20px auto;
    padding: 20px;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  }
  
  form {
    display: flex;
    margin-bottom: 20px;
  }
  
  input[type="text"] {
    flex: 1;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    font-size: 16px;
  }
  
  button[type="submit"] {
    padding: 10px 20px;
    background-color: #333;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
  }
  
  button[type="submit"]:hover {
    background-color: #555;
  }
  
  #list {
    border-top: 1px solid #ccc;
    padding-top: 20px;
  }

 

📘 restFront.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="utf-8" />
  <title>RESTful 서버 예제</title>
  <link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>
<div>
  <form id="form">
    <input type="text" id="username">
    <button type="submit">등록</button>
  </form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="./restFront.js"></script>
</body>
</html>

 

📘 about.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>RESTful 서버 예제</title>
  <link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>
<div>
  <h2>소개 페이지입니다.</h2>
  <p>사용자 이름을 등록하세요!</p>
</div>
</body>
</html>

 

📘 restFront.js

async function getUser() { // 로딩 시 사용자 정보를 가져오는 함수
    try {
        const res = await axios.get('/users');
        const users = res.data;
        const list = document.getElementById('list');
        list.innerHTML = '';

        // 사용자마다 반복적으로 화면 표시 및 이벤트 연결
        Object.keys(users).map(function (key) {
            const userDiv = document.createElement('div');
            const span = document.createElement('span');
            span.textContent = users[key];
            const edit = document.createElement('button');
            edit.textContent = '수정';
            edit.addEventListener('click', async () => { // 수정버튼 클릭
                const name = prompt('바꿀 이름을 입력하세요');
                if (!name) {
                    return alert('이름을 반드시 입력하여야 합니다.');
                }
                try {
                    await axios.put('/user/' + key, { name });
                    getUser();
                } catch (e) {
                    console.error(e);
                }
            });
            const remove = document.createElement('button');
            remove.textContent = '삭제';
            remove.addEventListener('click', async () => { // 삭제 버튼 클릭
                try {
                    await axios.delete('/user/' + key);
                    getUser();
                } catch (e) {
                    console.error(e);
                }
            });
            userDiv.appendChild(span);
            userDiv.appendChild(edit);
            userDiv.appendChild(remove);
            list.appendChild(userDiv);
            console.log(res.data);
        });
    } catch (e) {
        console.error(e);
    }
}

window.onload = getUser; // 화면 로딩 시 getUser 호출
// 폼 제출(submit) 시 실행하기
document.getElementById('form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const name = e.target.username.value;
    if (!name) {
        return alert('이름을 입력하세요');
    }
    try {
        await axios.post('/user', { name });
        getUser();
    } catch (e) {
        console.error(e);
    }
    e.target.username.value = '';
});

 

📘 restServer.js (핵심 부분)

  • req.method로 HTTP 요청 메서드를 구분하고 있습니다.
  • 메서드가 GET 이면 다시 req.url로 요청 주소를 구분합니다.
  • 주소가 '/' 일 때는 restFront.html을 제공하고, 주소가 '/about' 이면 about.html 파일을 제공합니다.
  • 만약 존재하지 않는 파일을 요청했거나 GET 메서드 요청이 아닌 경우라면 404 not found 에러가 응답으로 전송됩니다.
  • 데이터베이스 대용으로 users 라는 객체를 선언해 사용자 정보를 저장하였습니다.
  • POST와 PUT 요청을 처리할 때에는 req.on('data')와 req.on('end')의 사용인데 요청의 본문에 들어 있는 데이터를 꺼내기 위한 작업이라고 볼 수 있습니다.
  • req와 res도 내부적으로는 스트림(readStream과 writeStream)으로 되어 있으므로 요청/응답의 데이터가 스트림 형식으로 전달됩니다.
const http = require('http');
const fs = require('fs').promises;
const path = require('path');

const users = {}; // 데이터 저장용

http.createServer(async (req, res) => {
    try {
        console.log(req.method, req.url);
        if(req.method === 'GET') {
            if(req.url === '/') {
                const data = await fs.readFile(path.join(__dirname, 'restFront.html'));
                res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
                return res.end(data);
            } else if (req.url === '/about') {
                const data = await fs.readFile(path.join(__dirname, 'about.html'));
                res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
                return res.end(data);
            } else if (req.url === '/users') {
                res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8'});
                return res.end(JSON.stringify(users));
            }
            // 주소가 / 도 /about 도 아니면?
            try {
                const data = await fs.readFile(path.join(__dirname, req.url));
                return res.end(data);
            } catch (e) {
                // 주소에 해당하는 라우트를 찾지 못했다는 404 not found error 발생
            }
        } else if (req.method === 'POST') {
            if(req.url === '/user') {
                let body = '';
                // 요청의 body를 stream 형식으로 받음
                req.on('data', (data) => {
                    body += data;
                });
                // 요청의 body를 다 받은 후 실행됨
                return req.on('end', () => {
                    console.log('POST 본문(Body) : ', body);
                    const { name } = JSON.parse(body);
                    const id = Date.now();
                    users[id] = name;
                    res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8'});
                    res.end('등록 성공');
                });
            }
        } else if (req.method === 'PUT') {
            if (req.url.startsWith('/user/')) {
                const key = req.url.split('/')[2];
                let body = '';
                req.on('data', (data) => {
                    body += data;
                });
                return req.on('end', () => {
                    console.log('PUT 본문(Body):', body);
                    users[key] = JSON.parse(body).name;
                    res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8'});
                    return res.end(JSON.stringify(users));
                });
            }
        } else if (req.method === 'DELETE') {
            if (req.url.startsWith('/user/')) {
                const key = req.url.split('/')[2];
                delete users[key];
                res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8'});
                return res.end(JSON.stringify(users));
            }
        }
        res.writeHead(404);
        return res.end('NOT FOUND');
    } catch (e) {
        console.error(e);
        res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
        res.end(e.message);
    }
})
.listen(8082, () => {
    console.log('8082번 포트에서 서버 대기중 입니다.');
});

 

💡 서버 실행하기

프로젝트 파일이 있는 폴더에서 터미널을 실행하고 'node restServer' 명령어를 입력하여 서버를 실행합니다.

http://localhost:8082로 접속하면 아래와 같은 화면이 보입니다.

 

home 화면일 경우
about 페이지일 경우
user 등록(POST)

 

user 수정 (PUT)
user 삭제 (DELETE)

 

반응형

'Node.js' 카테고리의 다른 글

REPL  (0) 2024.05.06
반응형

💡 REPL 이란 무엇일까?

자바스크립트는 스크립트 언어입니다. 미리 컴파일하지 않아도 바로 코드를 실행할 수 있습니다.

노드에서도 비슷한 콘솔을 제공하는데요, 입력한 코드를 읽고(read), 해석하고(eval), 반환하고(print), 종료할 때까지 반복한다(loop)라고 해서 REPL(Read Eval Print Loop)이라고 합니다.

 

💡 노드의 REPL을 사용해보자!

그럼 노드의 REPL을 사용해볼까요?

윈도우에서는 명령 프롬프트, 맥이나 리눅스의 경우에는 터미널을 열고 node를 입력합니다.

 

이렇게 node를 입력하면 콘솔창 모양이 > 으로 바뀌게 됩니다. 여기에서 자바스크립트 코드를 입력할 수 있습니다.

간단한 예제코드를 입력해보겠습니다.

 

 

위와 같이 출력이된다면 성공입니다. 

입력한 코드를 읽고, 해석한 뒤 바로 결과물을 출력했습니다. 그리고 종료되기 전까지 입력을 기다립니다.

REPL을 종료하려면 Ctrl + C 를 두 번 누르거나, .exit를 입력하여 종료할 수 있습니다.

 

REPL은 한 두줄 정도의 코드를 테스트하기에는 좋지만, 여러 줄의 코드를 실행하기에는 불편합니다. 긴 코드의 경우에는 자바스크립트 파일을 생성한 후 파일을 통째로 실행하는 것이 편리합니다.

 

💡 자바스크립트 파일을 실행해보기

REPL에 직접 코드를 입력하는 대신에 자바스크립트 파일을 만들어 실행해보겠습니다.
저는 node라는 폴더 안에 자바스크립트 파일을 생성하였습니다.

 

function helloWorld() {
    console.log("Hello World");
    helloNode();
}

function helloNode() {
    console.log("Hello Node");
}

helloWorld();

 

그럼 이제 파일을 실행해 볼까요?

 

js파일이 있는 폴더에서 터미널을 실행하여 node (자바스크립트 파일 경로)를 입력하여 실행합니다. 확장자는 생략가능합니다.

여기에서 주의해야 할 점은 REPL에서 실행하는 것이 아닌 콘솔에서 실행한다는 점입니다.

콘솔에서 REPL로 들어가는 명령어가 node이고, 노드를 통해 파일을 실행하는 명령어가 node (자바스크립트 파일 경로) 입니다.

 

 

 

반응형

'Node.js' 카테고리의 다른 글

Node.js REST와 라우팅 사용하기  (0) 2024.05.06
반응형

위젯은 자식을 하나만 갖는 위젯과 자식을 여러개를 갖는 위젯으로 나뉩니다.
 
자식을 하나만 갖는 대표적인 위젯들은 아래와 같고, 대체로 child 매개변수를 입력받습니다.
 

  • Container 위젯 : 자식을 담는 컨테이너 역할을 합니다. 다만 단순하게 자식을 담는 역할을 하는게 아니라 배경색, 너비와 높이, 테두리 등의 디자인을 지정할 수 있습니다.
  • GestureDetector 위젯 : 플러터에서 제공하는 제스처 기능을 자식 위젯에서 인식하는 위젯, 탭이나 드래그 그리고 더블 클릭 같은 제스처 기능이 자식 위젯에 인식됐을 때 함수를 실행할 수 있습니다.
  • SizedBox 위젯 : 높이와 너비를 지정하는 위젯, Container 위젯과 다르게 디자인적 요소는 적용할 수 없고, const 생성자로 선언할 수 있어서 퍼포먼스 측면에서 더 효율적

 
다수의 자식을 입력할 수 있는 위젯은 children 매개변수를 입력받으며 리스트로 여러 위젯을 입력할 수 있습니다.
 

  • Column 위젯 : children 매개변수에 입력된 모든 위젯들을 세로로 배치
  • Row 위젯 : children 매개변수에 입력된 모두 위젯들을 가로로 배치
  • ListView 위젯 : 리스트를 구현할 때 사용, children 매개변수에 다수의 위젯을 입력할 수 있으며 입력된 위젯이 화면을 벗어나게 되면 스크롤이 가능

 

Children 와 Child의 차이점

child는 위젯을 하나만 추가할 수 있고, children은 여럿을 추가할 수 있습니다.
대부분의 위젯은 child 또는 children 매개변수를 하나만 제공합니다. child와 children 매개변수를 동시에 입력받는 위젯은 존재하지 않습니다.
 
 

제스처 관련 위젯

사용자가 키보드로 글자를 입력하는 행위 이외 모든 입력을 플러터에서는 제스처라고 부릅니다.
화면을 한 번 탭하거나, 두 번 탭하거나, 길게 누르는 행동 모두가 제스처 입니다.
GestureDetector 위젯은 모든 제스처를 매개변수로 제공해줍니다. 제스처 관련 위젯은 하위 위젯에 탭이나 드래그처럼 특정 제스처가 입력됬을 때 인지하고 콜백 함수를 실행합니다.
 

Button 위젯

머터리얼 패키지에서 기본 제공하는 버튼으로 TextButton, OutlinedButton, ElevatedButton이 있습니다. 이 세 개의 버튼은 모두 버튼을 누르면 색깔이 변경되는 리플 효과를 지원합니다.
 
🔷 TextButton

import 'package:flutter/material.dart';

void main(){
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: TextButton(
            // 클릭시 실행
            onPressed: (){
              print('버튼 눌림');
            },

            // 스타일 지정하기
            style: TextButton.styleFrom(
              // 주 색상 지정
              foregroundColor: Colors.red,
            ),
            child: Text('텍스트 버벝'),
          ),
        ),
      ),
    );
  }
}

 

 
 
🔷 OutlinedButton

import 'package:flutter/material.dart';

void main(){
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: OutlinedButton(
            // 클릭시 실행
            onPressed: (){
              print('버튼 눌림');
            },

            // 스타일 지정하기
            style: OutlinedButton.styleFrom(
              // 주 색상 지정
              foregroundColor: Colors.red,
            ),
            child: Text('아웃라인 버튼'),
          ),
        ),
      ),
    );
  }
}

 

 
 
🔷 ElevatedButton

import 'package:flutter/material.dart';

void main(){
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            // 클릭시 실행
            onPressed: (){
              print('버튼 눌림');
            },

            // 스타일 지정하기
            style: ElevatedButton.styleFrom(
              // 주 색상 지정
              backgroundColor: Colors.red,

            ),
            child: Text('엘리베이티드 버튼', style: TextStyle(color: Colors.white,),),
          ),
        ),
      ),
    );
  }
}

 

 

반응형

'Flutter' 카테고리의 다른 글

다트 이해하기 - (2) 내장 변수타입  (0) 2024.12.01
다트 이해하기 - (1) 변수  (0) 2024.12.01
typedef와 함수  (0) 2024.03.30
dart 이론  (1) 2024.03.30
TodoList App 만들기  (0) 2024.03.21
반응형

typedef 키워드는 함수의 시그니처를 정의하는 값으로 보면 됩니다.

여기서 시그니처란 반환값 타입, 매개변수 개수와 타입 등을 말합니다. 즉 함수 선언부를 정의하는 키워드입니다.

함수가 무슨 동작을 하는지에 대한 정의는 없습니다.

 

typedef Operation = void Function(int x, int y);

void add(int x, int y) {
  print('결과값 : ${x + y}');
}

void subtract(int x, int y) {
  print('결과값 : ${x - y}');
}


void main () {
  // typedef는 일반적인 변수의 타입처럼 사용가능
  Operation oper = add;
  oper(1, 2);

  // subract() 함수도 Operation에 해당되는 시그니처이므로 oper 변수에 저장 가능
  oper = subtract;
  oper(1, 2);
}
결과값 : 3
결과값 : -1
반응형

'Flutter' 카테고리의 다른 글

다트 이해하기 - (1) 변수  (0) 2024.12.01
플러터 기본위젯  (0) 2024.03.30
dart 이론  (1) 2024.03.30
TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
반응형

📘 var를 사용한 선언

변수는 var 변수명 = 값; 으로 선언합니다. 변수에 값이 들어가면 자동으로 타입을 추론하는 타입 추론 기능을 제공하므로 명시적으로 타입을 선언하지 않아도 됩니다.

void main (){
	
    var name = '주녘주녘';
    print(name);
    
    // 변수의 값 변경가능
    name = '주녘';
    print(name);
    
    // 변수명 중복은 불가
    // var name = '플러터의신'; <- 오류남

}

 

📘 dynamic을 사용한 변수선언

var 타입은 변수의 값을 사용해서 변수의 타입을 유추하는 키워드입니다. 타입을 한 번 유추하면 추론된 타입이 고정됩니다.

따라서 고정된 변수 타입과 다른 변수 타입의 값을 같은 변수에 다시 저장하면 에러가 발생합니다. 하지만 dynamic 키워드를 사용하면 변수의 타입이 고정되지 않아서 다른 타입의 값을 저장할 수 있습니다.

 

void main() {

	dynamic name = '주녘주녘';
	name = 1;

}

 

📘 final/const를 사용한 변수 선언

final과 const는 변수의 값을 처음 선언 후 변경할 수 없습니다.

void main () {

	final String name = '뉴진스';
 	name = '르세라핌';	// 에러발생 final로 선언한 변수는 선언 후 값을 변경 불가
    
    
  	const String name2 = '아이브';
 	name2 = '뉴진스';	// 에러발생 final로 선언한 변수는 선언 후 값을 변경 불가

}

 

- final은 런타임, const는 빌드타임 상수입니다.

- 코드를 실행하지 않은 상태에서 값이 확정되면 const를, 실행할 때 확정되면 final을 사용!

 

📘 컬렉션

여러 값을 하나의 변수에 저장할 수 있는 타입입니다.

여러 값을 순서대로 저장하거나(List), 특정 키값 기반으로 빠르게 값을 검색해야 하거나(Map), 중복된 데이터를 제거할 때(Set) 사용됩니다. 컬렉션 타입은 서로의 타입으로 자유롭게 형변환이 가능하다는 큰 장점이 있습니다.

 

🔷 List 타입

- 여러 값을 순서대로 한 변수에 저장할 때 사용

- 리스트의 구성 단위를 원소라고 합니다.

- 리스트명[인덱스] 형식으로 특정 원소에 접근 가능합니다.

- 인덱스는 원소의 순번

- 제일 첫 원소는 0으로 지정, 마지막 원소는 리스트 길이 -1 로 지정

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  print('----------- 전체 리스트를 출력해보자 -----------');
  print(iveList);
  print('----------- 첫번째 원소를 출력해보자 -----------');
  print(iveList[0]);
  print('----------- 다섯번째 원소를 출력해보자 -----------');
  print(iveList[4]);
  print('----------- 리스트 길이를 출력해보자 -----------');
  print(iveList.length);

  print('----------- 인덱스 값 변경 -----------');
  iveList[3] = '주녘';

  print('----------- 전체 리스트를 출력해보자 -----------');
  print(iveList);
}
출력값



----------- 전체 리스트를 출력해보자 -----------
[유진, 원영, 이서, 레이, 리즈, 가을]
----------- 첫번째 원소를 출력해보자 -----------
유진
----------- 다섯번째 원소를 출력해보자 -----------
리즈
----------- 리스트 길이를 출력해보자 -----------
6
----------- 인덱스 값 변경 -----------
----------- 전체 리스트를 출력해보자 -----------
[유진, 원영, 이서, 주녘, 리즈, 가을]

 

- 리스트 길이는 length를 가져와 확인할 수 있습니다.

 

📘 add() 함수

add() 함수는 List에 값을 추가할 때 사용되며 추가하고 싶은 값을 매개변수에 입력하면 됩니다.

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  iveList.add('주녘'); // 리스트의 끝에 추가됨

  print(iveList);
}
/Users/junhyuk/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/junhyuk/Desktop/untitled/lib/main.dart
[유진, 원영, 이서, 레이, 리즈, 가을, 주녘]

Process finished with exit code 0

 

📘 where() 함수

where() 함수는 List에 있는 값들을 순서대로 순회하면서 특정 조건에 맞는 값만 필터링하는데 사용됩니다.

매개변수에 함수를 입력해야하며, 입력된 함수는 기존 값을 하나씩 매개변수로 입력받습니다. 각 값별로 true를 반환하면 값을 유지하고, false를 반환하면 값을 버립니다. 순회가 끝나면 유지된 값들을 기반으로 이터러블이 반환됩니다.

 

* 이터러블?

- List나 Set 등의 컬렉션 타입들이 상속받는 클래스

- List나 Set 같은 컬렉션이 공통으로 사용하는 기능을 정의해둔 클래스

- where() 이나 map() 등 순서가 있는 값을 반환할 때 사용

 

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  final newList = iveList.where((name) => name == '유진' || name == '원영', );  // '유진' 또는 '원영' 만 유지

  print(newList);
  print(newList.toList()); // 이터러블을 리스트로 다시 반환할 때 toList 사용
}
/Users/junhyuk/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/junhyuk/Desktop/untitled/lib/main.dart
(유진, 원영)
[유진, 원영]

Process finished with exit code 0

 

📘 map() 함수

map 함수는 List에 있는 값들을 순서대로 순회하면서 값을 변경할 수 있습니다.

매개변수에 함수를 입력해야 하며 입력된 함수는 기존 값을 하나씩 매개변수로 받습니다. 반환하는 값이 현재값을 대체하며 순회가 끝나면 이터러블이 반환됩니다.

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  final newIveList = iveList.map((name) => '아이브 $name');  // 리스트의 모든 값 앞에 '아이브' 추가

  print(newIveList);

  print(newIveList.toList());
}
(아이브 유진, 아이브 원영, 아이브 이서, 아이브 레이, 아이브 리즈, 아이브 가을)
[아이브 유진, 아이브 원영, 아이브 이서, 아이브 레이, 아이브 리즈, 아이브 가을]

 

반응형

'Flutter' 카테고리의 다른 글

플러터 기본위젯  (0) 2024.03.30
typedef와 함수  (0) 2024.03.30
TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
반응형

🔹 프로젝트 기간 : 2024.03.11 ~ 2024.03.26 (총 12일)

🔹 Git : https://github.com/CyberUniversityProject/CyberUnivProject

 

GitHub - CyberUniversityProject/CyberUnivProject

Contribute to CyberUniversityProject/CyberUnivProject development by creating an account on GitHub.

github.com

🔹 PDF : https://drive.google.com/file/d/1htnvcKOFypTGXwcoz49CLErcanUjPlBU/view?usp=sharing

 

학사관리시스템 프로젝트(CU)_ 보고서

 

drive.google.com

 

 

 

🧑🏻‍💻 프로젝트 선정배경

  • 다양한 프로젝트를 거치면서 ERP 시스템에 대해 팀원 모두가 관심을 가지게 됨
  • 대학생 시절에 사용해본 학사관리시스템을 주제로 선정하여 개발 프로젝트 진행

🧑🏻‍💻 역할

  • 프로젝트 팀장 역할
  • 사이트 기본 디자인 구현
  • 로그인/로그아웃/ID/PW 찾기
  • 교직원 기능
    1. 학생/교수 명단조회
    2. 학생/교수/교직원 등록
    3. 등록금 고지서 발송
    4. 휴학 처리
    5. 수강신청기간 설정
    6. 강의 등록
    7. 단대별 등록금 설정
  • 학생 기능
    1. 수강신청
  • 커뮤니티
  • 캠퍼스맵

 

 

🛠️ 기술 스택

🔻 Front-End

  • HTML5
  • CSS3
  • JavaScript (1.16.1)
  • JQuery (3.6.4)
  • bootstrap (4.6.2)

🔻 Back-End

  • JAVA 17
  • SpringBoot (3.1.8)
  • MyBatis
  • JSP
  • MySQL (8.0.26)
  • lombok
  • Apache Tomcat(10.1.19)

🔻 API

  • 포트원 결제 API
  • 나이스 식단 API
  • 카카오맵 API

🔻 Collaboration

  • Git
  • GitHub Desktop
  • Discord
  • Notion
  • Slack

 

📖 전체 기능 둘러보기

https://youtu.be/G59X8qBfSrg?si=2Mh7UqbNjgkcKI10

 

 

 

📖 주요 담당 기능

🔹 아이디/비밀번호 찾기

  • 이름과 이메일 그리고 계정 유형을 선택하여 해당하는 유저가 있는 경우 아이디 찾기 결과를 보여줌
  • 비밀번호 찾기는 이름과 (아이디 학번(사번)) 이메일을 입력하여 임시비밀번호를 발급하는 형태
  • 일치하는 회원정보가 있다면 임시비밀번호 발급, 임시발급된 비밀번호를 입력하여 로그인

🔹 학생, 교수 명단조회 + 검색기능

https://youtu.be/BP67nSln-aM?si=QIcM-G5nhwsaGnXs

  • 등록된 학생, 교수들의 목록을 확인할 수 있음
  • 학과 번호 또는 학번 검색 기능

🔹 학생, 교수, 교직원 등록

https://youtu.be/IpTRca01Cik?si=ivBCTcWhuPlCB4w4

  • 유저 별 계정 생성 시 직책에 따라 DB에 생성됨
  • 학생의 경우 사진 이미지 업로드 가능

🔹 등록금 고지서 전송

https://youtu.be/gXovzd8V_iM?si=73zckNHgbrlzuV71

  • 교직원이 등록금 고지서 전송 버튼을 누르면 이번 학기까지 재학중인 학생들에게 등록금 고지서 발송
  • 고지서가 생성되었다는 알림창이 뜨게 되며 학생 로그인 > 등록금 납부 조회페이지에서 확인 가능

🔹 휴학처리

https://youtu.be/tcoHlq5n7w4?si=WrRWcdRZMKSM3WkH

  • 학생이 휴학신청에서 휴학원 양식을 작성 후 신청을 하면 교직원이 신청 내역을 확인할 수 있음
  • 교직원이 신청 내역을 확인 후 휴학 승인 또는 반려처리

🔷 수강신청기간 설정

https://youtu.be/exCFo_XNTDU?si=QKVM3C98vyE5K6JJ

  • 기본값은 수강신청기간 종료
  • 예비수강신청 > 수강신청 > 종료 순으로 변경

https://youtu.be/pU1mA1hMil8

[예비 수강신청]

  • 대상 : 현재 학기에 재학 상태가 되는 학생
  • 신청/취소할 때마다 강의 현재 인원 변경
  • 신청 강의의 정원 초과 가능 (찜하기 기능과 유사)
  • 최대 수강 가능 학점 초과 불가능 (최대 18학점)
  • 신청자 본인의 시간표와 겹치는 강의 신청 불가능

[수강신청]

  • 정원 ≥ 신청인원인 강의 : 예비 수강 신청 내역이 수강 신청 내역으로 자동으로 이월됨
  • 정원 < 신청인원인 강의 : 신청 인원이 0명으로 초기화되며, 학생이 직접 신청해야 함
  • 예비 수강 신청 내역이 있는 경우, 신청이 용이하도록 수강 신청 탭에 가장 먼저 출력됨
  • 수강신청 대상 : 현재 학기에 재학 상태가 되는 학생
  • 신청/취소할 때마다 강의 현재 인원 변경
  • 신청 강의의 정원 초과 불가능
  • 최대 수강 가능 학점 초과 불가능 (최대 18학점)
  • 신청자 본인의 시간표와 겹치는 강의 신청 불가능
  • 페이징 처리, 검색 기능
반응형
반응형
🚩TodoList App 만들기 목표
1. 폴더 구조 잡기
2. main 파일 만들어 보기
3. Model 클래스 생성해보기
4. View 만들어 보기
5. ViewModel 만들어 보기
6. view(todo_list_view.dart) 에 데이터 분리 하기

 

 

1. 프로젝트 구조 구성하기

2. main.dart 코드 작성하기

import 'package:flutter/material.dart';

void main() {
  runApp(const TodoApp());
}

class TodoApp extends StatelessWidget {
  const TodoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(title: const Text("TodoList"),),
          body: Center(
            child: Text("My Todo"),
          ),
        ),
      ),
    );
  }
}

 

import 'package:flutter/material.dart';
import 'package:my_todo_mvvm/views/todo_list_view.dart';

void main() {
  runApp(const TodoApp());
}

class TodoApp extends StatelessWidget {
  const TodoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(title: const Text("TodoList"),),
          body: TodoListView(),
        ),
      ),
    );
  }
}

 

3. Model 클래스 생성해보기

/models/todo_item.dart 파일 생성하기

// Model
class TodoItem {
  String title;
  bool isDone;

  TodoItem({required this.title, this.isDone = false});
}

 

 

4. view 만들어 보기

/views/todo_list_view.dart 파일 생성하기 (1단계)

import 'package:flutter/material.dart';


// View 클래스 

class TodoListView extends StatefulWidget {
  const TodoListView({super.key});

  @override
  State<TodoListView> createState() => _TodoListViewState();
} // end of TodoListView class


class _TodoListViewState extends State<TodoListView> {

  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: 'Enter todo item...',
              suffix: IconButton(
                icon: Icon(Icons.add),
                onPressed: () {
                 setState(() {
                   // build() 메서드 재 호출
                   _controller.clear();
                 });

                },
              )
            ),
          ),


        ],
      ),
    );
  }
} // end of _TodoListViewState

 

TextEditingController는 TextField 위젯에서 사용자 입력을 관리하는데 사용되는 클래스입니다. 이를 통해 TextField에서 사용자가 입력한 값에 접근하거나, TextField의 값을 변경하거나, TextField를 초기화하는 등 다양한 작업을 수행할 수 있습니다.

  1. 입력값 접근:
    • *TextEditingController*는 _controller.text 프로퍼티를 통해 현재 **TextField*에 입력된 값을 가져올 수 있습니다. 사용자가 **TextField*에 입력한 내용을 가져와서 로직에 활용하거나 다른 곳에 표시할 수 있습니다.
  2. 입력값 변경:
    • *_controller.text = 'newValue'*와 같이 **TextEditingController*의 text 프로퍼티를 통해 **TextField*의 값을 프로그래밍 방식으로 변경할 수 있습니다.
  3. 입력값 초기화:
    • *TextEditingController*의 clear 메서드를 사용하여 **TextField*의 내용을 쉽게 지울 수 있습니다. 이 기능은 사용자가 입력을 완료한 후 입력 필드를 초기화할 때 유용합니다.
  4. 입력 변경 감지:
    • *TextEditingController*에 리스너를 추가하여 **TextField*의 값이 변경될 때마다 알림을 받을 수 있습니다. 이를 통해 입력값이 변경될 때 특정 동작을 수행하도록 설정할 수 있습니다.

 

화면 만들기 2단계

import 'package:flutter/material.dart';
import 'package:my_todo_mvvm/models/todo_item.dart';

// View 클래스

class TodoListView extends StatefulWidget {
  const TodoListView({super.key});

  @override
  State<TodoListView> createState() => _TodoListViewState();
} // end of TodoListView class

class _TodoListViewState extends State<TodoListView> {
  final TextEditingController _controller = TextEditingController();

  // 샘플 데이터 만들어 보기
  List<TodoItem> _todoItems = [
    TodoItem(title: '플러터 공부하기', isDone: false),
    TodoItem(title: '낮잠 자기', isDone: true),
  ];

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: 'Enter todo item...',
              suffix: IconButton(
                icon: Icon(Icons.add),
                onPressed: () {
                  setState(() {
                    // build() 메서드 재 호출
                    _controller.clear();
                  });
                },
              ),
            ),
          ),
          //
          Expanded(
            child: ListView.builder(
              itemCount: _todoItems.length,
              itemBuilder: (context, index) {
                var item = _todoItems[index];
                // 두개의 인수 값을 받아서 위젯을 리턴 시키면 된다.
                return ListTile(
                  title: Text(item.title),
                  trailing: Checkbox(
                    value: item.isDone,
                    onChanged: (value) {
                      print("value : ${value}");
                      setState(() {
                        _todoItems[index].isDone = value ?? false;
                      });
                    },
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
} // end of _TodoListViewState

 

ViewModel 만들기

 

// ViewModel
import 'package:my_todo_mvvm/models/todo_item.dart';

class TodoListViewModel {

  // 화면 사용될 데이터
  List<TodoItem> _items = []; // private

  // get 메서드 만들어 주기
  List<TodoItem> get items => _items;

  // 리스트에 TodoItem 객체를 추가하는 메서드 만들기
  void addItem(String title) {
    _items.add(TodoItem(title: title, isDone: false));
  }

  void toggleItem(TodoItem todo) {
    todo.isDone = !todo.isDone;
  }

}

 

todo_list_view.dart 수정

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_todo_mvvm/models/todo_item.dart';
import 'package:my_todo_mvvm/view_models/todo_list_view_model.dart';

// View 클래스

class TodoListView extends StatefulWidget {
  const TodoListView({super.key});

  @override
  State<TodoListView> createState() => _TodoListViewState();
} // end of class

class _TodoListViewState extends State<TodoListView> {

  final TextEditingController _controller = TextEditingController();
  final TodoListViewModel listViewModel = TodoListViewModel();





  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: 'Enter Todo Item',
              suffix: IconButton(
                icon: Icon(Icons.add),
                onPressed: (){
                  setState(() {
                    listViewModel.addItem(_controller.text);
                    _controller.clear();
                  });
                },
              )
            ),
          ),
          //
          Expanded(
            child: ListView.builder(
              itemCount: listViewModel.items.length,
              itemBuilder: (context, index){
                var item = listViewModel.items[index];
                // 두개 인수값을 받아서 위젯을 리턴
                return ListTile(
                  title: Text(item.title),
                  trailing: Checkbox(
                    value: item.isDone,
                    onChanged: (value) {
                      print('value : ${value}');
                      setState(() {
                        listViewModel.toggleItem(item);
                      });
                    },
                  ),
                );
            },),
          )

        ],
      ),
    );
  }
} // end of _TodoListViewState

 

 

반응형

'Flutter' 카테고리의 다른 글

typedef와 함수  (0) 2024.03.30
dart 이론  (1) 2024.03.30
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
반응형

1. 앱 아키텍처란?

앱 아키텍처는 애플리케이션의 전반적인 구조와 구성요소, 그리고 이러한 구성요소 간의 관계와 상호작용을 정의하는 청사진 또는 설계 원칙을 의미합니다. 쉽게 말해, 앱을 구축하는 데 필요한 구성요소와 이러한 구성요소들이 어떻게 함께 작동하고 연결되는지를 설명하는 방법론이라고 할 수 있습니다.

MVC, MVP, MVVM, VIPER 등 너무나 많은 아키텍처가 존재 한다. 하지만 원리는 거의 동일 하다.

역할 별로 레이어를 나눈다.
각 레이어는 각자의 역할에만 집중하게 설계하고 자신 밖에 업무에서 가능한 신경을 끈다.
각 레이어를 나누게 되면 수정 및 테스트 유지 보수가 용이하다.

 

 

2. MVC (Model-View-Controller) 패턴을 사용하는 주된 이유는?

MVC (Model-View-Controller) 패턴은 오랫동안 사용되어온 소프트웨어 디자인 패턴 중 하나입니다. 웹 애플리케이션, 데스크톱 애플리케이션, 그리고 최근에는 모바일 애플리케이션에서도 널리 사용됩니다.

  1. 가독성: 각 구성요소(Model, View, Controller)가 독립적이기 때문에 코드의 구조가 명확해져서 가독성이 향상됩니다.
  2. 확장성: MVC 패턴은 기능의 확장이 필요할 때, 해당하는 부분만을 수정하면 되기 때문에 확장성이 좋습니다. 예를 들면, UI를 변경하고자 할 때 View만 수정하면 되고, 데이터 처리 방식을 변경하고자 할 때는 Model만 수정하면 됩니다.
  3. 재사용성: 각 구성요소가 독립적이므로 재사용이 용이합니다. 특히 Model은 다른 시스템이나 프로젝트에서도 재사용할 수 있습니다.
  4. 분리와 집중: 각 구성요소의 역할이 분리되어 있기 때문에 각 역할에 집중할 수 있습니다. 이로 인해 코드의 품질과 유지 보수성이 향상됩니다.

이러한 이유로 많은 소프트웨어 개발 프로젝트에서 MVC 패턴을 선호하고 사용합니다.

 

 

3. MVVM 패턴에 대해 알아 보자

  • Model: 실제 데이터 및 비즈니스 로직을 포함합니다. 데이터베이스 액세스, 웹 서비스 호출 등의 작업을 수행합니다.
  • View: 사용자에게 보여지는 UI 요소. 버튼, 텍스트박스, 라벨 등의 위젯 또는 컴포넌트를 포함합니다. View는 ViewModel에 직접적으로 연결되어 데이터를 가져옵니다.
  • ViewModel: Model과 View 사이의 중개자 역할을 합니다. Model에서 데이터를 가져오고 View에 표시할 준비를 담당합니다. 또한 View에서 이벤트를 수신하고 그에 따라 Model을 업데이트합니다.

 

 

1단계 : 시나리오 코드 1 - MV 패턴으로 코드 만들어 보기

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeView(),
    );
  }
}

// View
class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {

  // 핵심 데이터 선정 - 클래스
  // Model 에서 데이터와 관련된 로직이 포함 된다.
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('뷰 모델 없는 코드를 작성해보기'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('간단한 뷰와 모델 예제'),
            Text('숫자 확인(핵심데이터) ${_counter}'),
            ElevatedButton(
              onPressed: () {
                // 사용자가 버튼을 클릭하면 숫자가 올라가는 기능을 만들자
                setState(() {
                  _counter++;
                });
              },
              child: Text('증감'),
            )
          ],
        ),
      ),
    );
  }
}

증감 버튼을 클릭시 숫자가 증가하는 것을 볼 수 있음

 

1단계에서는 MV 개념으로 코딩을 했다면 ViewModel 이라는 개념을 적용해보자

 

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeView(),
    );
  }
}

// View
class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {

  // 뷰에서는 뷰 모델만 바라보면 된다.
  final CounterViewModel viewModel = CounterViewModel();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('뷰 모델 없는 코드를 작성해보기'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('간단한 뷰와 모델 예제'),
            Text('숫자 확인(핵심데이터) ${viewModel.counter}'),
            ElevatedButton(
              onPressed: () {
                // 사용자가 버튼을 클릭하면 숫자가 올라가는 기능을 만들자
                setState(() {
                 viewModel.incrementCounter();
                });
              },
              child: Text('증감'),
            )
          ],
        ),
      ),
    );
  }
}


// 1단계에서는 MV 개념으로 코딩을 했다면 ViewModel 이라는 개념을 적용해보자
// viewModel
class CounterViewModel {
  int _counter = 0;
  int get counter => _counter;

  void incrementCounter(){
    _counter++;
  }
}
반응형

'Flutter' 카테고리의 다른 글

dart 이론  (1) 2024.03.30
TodoList App 만들기  (0) 2024.03.21
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
dart 비동기 프로그래밍 -1  (0) 2024.03.14
반응형

이번 팀 프로젝트는 도서관 플랫폼으로 주제를 정하고 사이트 이름은 북허브(BookHub)로 정하였습니다.

작업기간은 2024.02.13 ~ 2024.03.05 (총 15일) 소요되었습니다.

 

이번 프로젝트에서는 저는 부팀장역할을 맡아 사이트 전체 프론트 디자인 부분과

발표 PPT 제작 및

관리자페이지, 포인트 결제 기능부분을 맡아 개발을 진행하였습니다.

 

💡 프로젝트 선정 배경

  • 환경을 생각한 도서 서비스 → 책 생산시 낭비되는 종이를 막기 위해 책 공유 기능을 넣어 자원낭비를 최소화
  • 모임을 개설하여 서로의 의견을 주고 받을 수 있는 모임 서비스
  • 포인트 결제 수수료, 배너광고 수익을 통한 사이트 수익창출
  • 도서 검색, 대출/반납, 도서관 내 포인트를 이용한 베이커리, PC 사용 서비스

 

💡 내가 개발한 기능

  • 메인페이지 전체 디자인
  • 포트원 결제 API를 이용한 포인트상품 결제/환불 기능 구현(사이트 수익모델)
  • 관리자페이지 광고배너 등록/수정/삭제
  • 관리자페이지 상품 등록/수정/삭제

 

💡 기능 개발하면서 힘들었던점

  • 결제 환불 부분에서 직접 api를 만들면서 첫번째 환불 내역은 취소가 되는데 두번째...n번째 내역은 취소가 안되는 오류가 발생해 밤 낮으로 고민하고 검색하고 할 수 있는 방법을 찾은 결과 정상적으로 환불요청 구현을 완료하여서 뿌듯하였음

 

🛠️ 기술 스택

🔻 Front-End

  • HTML5
  • CSS3
  • JavaScript : 1.16.1
  • JQuery : 3.6.4
  • bootstrap : 4.6.2

🔻 Back-End

  • SpringBoot 3.1.8
  • JAVA 17
  • MySQL 8.0.26
  • lombok
  • Apache Tomcat : 9.0
  • MyBatis
  • JSP

🔻 API

  • OAuth2.0 (구글, 카카오, 네이버)
  • 포트원 결제 API
  • 카카오 주소 API

🔻 Collaboration

  • Git
  • GitBash
  • Discode
  • Notion

💡 전체 기능 시연 영상

📖 주요 담당 기능 미리보기

🔷 메인페이지

  • 웹 페이지를 개발하면서 가장 공들인 부분
  • 사이트 로고도 직접제작하여 실제로 운영하는 사이트 처럼 디자인함

 

🔷 포트원 API를 이용한 포인트 상품 결제

  • 저번 게임쇼핑몰 프로젝트에는 토스페이먼츠 api를 이용하였는데 확실히 포트원 api가 사용하기가 쉬웠다.

 

🔷 포트원 API를 이용한 포인트 상품 환불

  • 가장 시간을 많이 쓴 작업
  • 환불로직에 대한 이해가 부족해서 몇번이고 공식문서를 정독해서 완성한 기능이자 애착이 가는 기능

 

🔷 관리자 페이지 배너 등록

  • 수익 모델을 생각하다가, 배너 광고 클릭시 클릭 횟수마다 금액을 지정하면 괜찮을거 같아서 만든 기능
  • 한번 클릭시마다 550원이 광고수익으로 넘어간다.

 

 

🔷 관리자 페이지 상품 등록

  • 위에서 봤던 포인트 결제 상품들을 등록하는 관리자 페이지 부분
  • BookHub 포인트 상품을 추가하는 관리자 페이지 부분
  • 관리자 계정으로 로그인 후 포인트 관리 → 포인트 상품 등록 페이지에서 해당상품명, 포인트, 실제 결제될 가격 입력 후 등록
  • 메인 페이지 포인트 구매하기 페이지에 실제 등록된 상품이 보이게 구현

 

✅  총평

  • 1차 프로젝트이다 보니 부족한 부분이 많았고, 구현했다가 시간 부족으로 폐기한 기능들이 많아 아쉬웠다.
  • 팀원들끼리 남아서 서로 피드백하고 회의하면서 작업하는게 작업 퀄리티를 높힐 수 있어서 좋았다.
반응형
반응형
1. JSON 응답 타입을 확인 하자
2. JSON Object Type 의 이해 (동기적 방식 처리)
3. JSON Array Type 의 이해 (동기적 방식 처리)

 

 

1. JSON 응답 타입을 확인 하자 - Resposne → body(String)

// Json Object type -> { "key" : "value"}
// Json Array type -> [{},{},{} ]

 

 

Todo DTO

// TODO : DTO 개념 클래스를 설계할 때 nullable 타입으로 설계하자

class Todo {
  int? userId;
  int? id;
  String? title;
  bool? completed;

  // 기본 생성자
  // 강제성 - 생성자
  Todo(this.userId, this.id, this.title, this.completed);

  // 명명된 생성자2 - Map을 넣으면 Todo 오브젝트가 반환되는 코드를 작성
  // 이니셜 라이져 (변수를 초기화 해주는 문법)
  Todo.fromJson(Map<String, dynamic> json)
      : userId = json['userId'],
        id = json['id'],
        title = json['title'],
        completed = json['completed'];

  @override
  String toString() {
    return '내가 보는 - Todo{userId: $userId, id: $id, title: $title, completed: $completed}';
  }
}

 

import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:dart_future_v1/todo.dart';

void main() async {
  var res = await fetchTodo();

  if (res.statusCode == 200) {
    print("통신 성공");
    Map<String, dynamic> result = json.decode(res.body);
    var todo1 = Todo.fromJson(result);

    print("title : ${todo1.title} ");
    print("completed : ${todo1.completed} ");
  } else {
    print("통신 실패");
  }
}

// 통신을 하는 함수 만들어 보기
Future<http.Response> fetchTodo() async {
  const url = 'https://jsonplaceholder.typicode.com/todos/1';

  final response = await http.get(Uri.parse(url));

  return response;
}

 

실행결과

통신 성공
title : delectus aut autem 
completed : false 

Process finished with exit code 0

 

 

이번에는 user 정보를 파싱해서 출력해보자.

 

 

User.dart

// TODO : DTO 개념 클래스를 설계할 때 nullable 타입으로 설계하자

import 'address.dart';
import 'company.dart';

class User {
  int? id;
  String? name;
  String? username;
  String? email;
  Address? address;
  String? phone;
  String? website;
  Company? company;

  User(this.id, this.name, this.username, this.email, this.address, this.phone,
      this.website, this.company);

  User.fromJson(Map<String, dynamic> json)
      : id = json['id'],
        name = json['name'],
        username = json['username'],
        email = json['email'],
        address = json['address'] != null ? Address.fromJson(json['address']) : null,
        phone = json['phone'],
        website = json['website'],
        company = json['company'] != null ? Company.fromJson(json['company']) : null;

  @override
  String toString() {
    return 'User{id: $id, name: $name, username: $username, email: $email, address: $address, phone: $phone, website: $website, company: $company}';
  }
}

 

 

address.dart

import 'geo.dart';

class Address {
  String? street;
  String? suite;
  String? city;
  String? zipcode;
  Geo? geo;

  Address({this.street, this.suite, this.city, this.zipcode, this.geo});

  Address.fromJson(Map<String, dynamic> json)
      : street = json['street'],
        suite = json['suite'],
        city = json['city'],
        zipcode = json['zipcode'],
        geo = json['geo'] != null ? Geo.fromJson(json['geo']) : null;

  @override
  String toString() {
    return 'Address{street: $street, suite: $suite, city: $city, zipcode: $zipcode, geo: $geo}';
  }
}

 

geo.dart

class Geo {
  String? lat;
  String? lng;

  Geo({this.lat, this.lng});

  Geo.fromJson(Map<String, dynamic> json)
      : lat = json['lat'],
        lng = json['lng'];

  @override
  String toString() {
    return 'Geo{lat: $lat, lng: $lng}';
  }
}

 

 

main.dart

import 'dart:convert';

import 'package:dart_future_v1/user.dart';
import 'package:http/http.dart' as http;

void main() async {


  var res = await fetchUser();

  if(res.statusCode == 200) {
    print("통신 성공");
    Map<String, dynamic> result = json.decode(res.body);
    var user1 = User.fromJson(result);

    print("id : ${user1.id} ");
    print("name : ${user1.name} ");
    print("username : ${user1.username} ");
    print("email : ${user1.email} ");
    print("address : ${user1.address} ");
    print("phone : ${user1.phone} ");
    print("website : ${user1.website} ");
    print("company : ${user1.company} ");


  } else {
    print("통신 실패");
  }





}


// 통신을 하는 함수 만들어 보기
Future<http.Response> fetchUser() async {
  const url = 'https://jsonplaceholder.typicode.com/users/1';

  final response = await http.get(Uri.parse(url));

  return response;
}

 

 

실행결과


C:/devtool/flutter/bin/cache/dart-sdk/bin/dart.exe --enable-asserts C:\devtool\class_flutter\dart_future_v1\lib\main11.dart
통신 성공
id : 1 
name : Leanne Graham 
username : Bret 
email : Sincere@april.biz 
address : Address{street: Kulas Light, suite: Apt. 556, city: Gwenborough, zipcode: 92998-3874, geo: Geo{lat: -37.3159, lng: 81.1496}} 
phone : 1-770-736-8031 x56442 
website : hildegard.org 
company : Company{name: Romaguera-Crona, catchPhrase: Multi-layered client-server neural-net, bs: harness real-time e-markets} 

Process finished with exit code 0
반응형

'Flutter' 카테고리의 다른 글

TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
dart 비동기 프로그래밍 -1  (0) 2024.03.14
블로그 웹 앱 만들어보기  (0) 2023.12.15

+ Recent posts