본문으로 건너뛰기

오늘 한 일

  • Rust 공부(강의)
  • 약알림 앱 약 등록 페이지 UI 구현
  • 약알림 앱 메인 페이지 디자인 구현중


🦀 Rust 실습 - 영수증 관리 프로그램

// Project 1: Interactive bill manager
//
// User stories:
// * I want to add bills, including the name and amount owed.
// * I want to view existing bills.
// * I want to remove bills.
// * I want to edit existing bills.
// * I want to go back if I change my mind.

use std::io;
use std::io::{stdout, Write};

fn main() {
user_interactive();
}

//구조체 정의
struct Bill{
name: String,
owed: f64,
num: i32,
}
impl Bill{
fn new(name: &str, owed: f64, num: i32) -> Self{
Self{
name: name.to_owned(),
owed: owed,
num: num
}
}
}

//INTERACTIVE - 사용자 소통 로직
fn user_interactive() -> Result<(), io::Error> {
let mut bills: Vec<Bill> = vec![];
let mut BILL_NUMS = 0;

loop{
println!("1:영수증 추가, 2:영수증 조회, 3:영수증 삭제, 4:영수증 수정, 5:이전 작업 취소");
print!("어떤 작업을 하시겠어요? => ");
io::stdout().flush();

let user_input = get_input()?;
match user_input.as_str() {
"1" => add_bill(&mut bills, &mut BILL_NUMS),
"2" => read_bill(&mut bills),
"3" => delete_bill(&mut bills),
"4" => update_bill(&mut bills, &mut BILL_NUMS),
_ => println!("not impl yet"),
}

println!("");
}

}

// 사용자 입력값 받아서 Result<String>로 반환 함수
fn get_input() -> Result<String, io::Error> {
let mut buffer = String::new();
io::stdin().read_line(&mut buffer)?;

Ok(buffer.trim().to_owned())
}

//ADD 구현
fn add_bill(bills: &mut Vec<Bill>, BILL_NUMS: &mut i32) {
let mut result: Result<String, io::Error>;
let mut name = String::new();
let mut owed: f64 = 0.0;

print!("이름: ");
io::stdout().flush();
result = get_input();
match result{
Ok(buffer) => {
name = buffer;
}
Err(_) => {
println!("Add failed - IO issue");
}
}
print!("가격: ");
io::stdout().flush();
result = get_input();
match result{
Ok(buffer) => {
owed = buffer.parse().unwrap();
}
Err(_) => {
println!("Add failed - IO issue");
}
}

let bill = Bill::new(&name, owed, *BILL_NUMS);
*BILL_NUMS += 1;
bills.push(bill);
}

// READ 구현
fn read_bill(bills: &Vec<Bill>){
let mut result: Result<String, io::Error>;
let mut i: i32 = 0;

print!("이름: ");
io::stdout().flush();
result = get_input();

match result{
Ok(name) => {

for bill in bills{
if bill.name == name{
println!("[{:?}] #{:?} name: {:?}, owed: {:?}", i, bill.num, bill.name, bill.owed);
i+=1;
}
}
if i == 0 {
println!("name: {:?} bill NOT FOUND!", name);
}
}
Err(_) => {
println!("Read failed - IO issue");
}
}
}

// Delete 구현
fn delete_bill(bills: &mut Vec<Bill>){
let mut result: Result<String, io::Error>;
let mut bill_num = -1;
let mut index = 0;

print!("삭제할 영수증 번호: ");
io::stdout().flush();
result = get_input();

match result{
Ok(num) => {
bill_num = num.parse().unwrap();

for bill in &mut *bills{
if bill.num == bill_num{
bills.remove(index);
println!("Delete #{:?}", bill_num);
break;
}
index+=1;
}
if index == bills.len(){
println!("#{:?} does not exist", bill_num);
}
}
Err(_) => {
println!("Add failed - IO issue");
}
}


}

// UPDATE 구현
fn update_bill(bills: &mut Vec<Bill>, BILL_NUMS: &mut i32){
let mut result: Result<String, io::Error>;
let mut bill_num = -1;
let mut price:f64 = 0.0;
let mut index = 0;
let mut exist = false;

print!("바꿀 영수증 번호: ");
io::stdout().flush();
result = get_input();

match result{
Ok(num) => {
bill_num = num.parse().unwrap();

for bill in &mut *bills{
if bill.num == bill_num{
println!("#{:?} name: {:?}, owed: {:?}", bill.num, bill.name, bill.owed);
exist = true;
break;
}
index+=1;
}
if !exist{
println!("#{:?} does not exist", bill_num);
}
}
Err(_) => {
println!("Add failed - IO issue");
}
}

if exist{
print!("수정할 금액: ");
io::stdout().flush();
result = get_input();

match result{
Ok(prices) => {
price = prices.parse().unwrap();
let bill: Bill = Bill::new(
bills[index].name.as_str(),
price,
bills[index].num,
);

bills.remove(index);
bills.push(bill);

println!("=> owed: {:?}", price);
}
Err(_) => {
println!("Add failed - IO issue");
}
}
}
}

// Roll back
미구현

자체 피드백

  1. add, remove, read의 재사용성을 고려했어야한다. - update에서 모두 사용되기 때문

    add/remove/read 함수 내부에서 사용자 입력값을 받아서 핸들링
    → 입력 관리 buffer가 함수의 인자로 거쳐지지 않고 간단한 구조로 짜고자 했음

    함수 외부에서 사용자 입력값을 버퍼에 저장하고 인자값으로 넘겨주어 값을 핸들링했어야한다.
    그 이유는 인자값을 받는 함수를 사용하면 사용자가 입력하지 않아도 내부적으로 관리할 때 그 로직이 필요하면 사용할 수 있게하기 위해서이다. → 수정은 귀찮으니 로직만 이렇게 생각해두자.


  2. Collection 구조로 vector를 선택했다.

    HashMap으로는 key의 충돌이 발생할 수 있다고 생각했기 때문

    ⇒ key를 사람: String이 아닌 bill num: i32으로 했다면 깔끔하게 정리됐을 것 같다.

    ⇒ key가 사람 이름인게 추상화로는 옳지만 컴퓨터 내부 관리용으로 HashMap도 나쁘지 않은 것 같다.
    그렇다고 vec보다 HashMap이 좋냐고 물어보면 나는 잘 모르겠다.



💊 약 등록 페이지 UI 구현

🎯 Button 색상 변경

: 기본적으로 theme을 쓴다면 default color는 android:colorAccent 라고 한다.

[case 1] Button Style이 없는 경우
android:backgroudTint = "colors/SOMETHING"

[case 2] Button Style을 설정해준 경우
→ 미해결
⇒ 버튼 UI로 만든 후 클릭시 상태 변화를 어떻게할지 생각만해도 머리아프다.


🎯 Row(LinearLayout-horizontal) 최대간격으로 만들기

[시도 1] LinearLayout gravity를 쓰는 경우
→ 못찾았다.

[시도 2] Flutter의 Extended와 같이 빈공간을 모두 차지할 위젯을 찾는다.
⇒ weight를 중간 Space 컴포넌트만 1로 설정함으로써 해결

[재욱님의 피드백] LinearLayout보단 ConstraintLayout으로 스크린의 상대적 위치에 따라 배치하는 것을 추천
⇒ Linear로 배치하는 경우 Linear에서 사용되는 컴포넌트-children-의 크기를 동적으로 핸들링하기가 힘들기 때문에
스크린의 크기가 달라질 때 예상치 못하게 위치가 깨질 수 있다.
=> 이 피드백을 고려해봤을 때, Linear는 ScrollView와 같이 명확하게 필요한 상황에서만 사용하는 게 좋을 것 같다.


🎯 Material Icon 리소스로 등록하기

: drawable > new > vector image > 검색 후 등록



🎨 메인페이지 디자인 중

스크린샷 2023-07-04 오후 10 24 29