묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 3회차 과제
데이터베이스 고윳값 제약으로 예방하는 구현에 대해서 조사했습니다. 동일 일자와 동일 타임슬롯인 경우 중복Booking에 unique index를 생성하는 것인데 동일 일자 (when)과 동일 타임슬롯(time_slot_id) 조건을 생성하면 됩니다. CREATE UNIQUE INDEX uq_booking_slot ON bookings (when, time_slot_id);class Booking(SQLModel, table=True): __tablename__ = "bookings" __table_args__ = ( UniqueConstraint("when", "time_slot_id", name="uq_booking_slot"), ) attendance_status 필드값이 cancelled인 경우PostgreSQL의 경우 partial unique index가 가능합니다. where를 사용해서 field가 cancelled가 아닌 경우에 unique 제약을 걸면 됩니다. CREATE UNIQUE INDEX uq_booking_slot ON bookings (when, time_slot_id) WHERE attendance_status <> 'cancelled' ;class Booking(SQLModel, table=True): __tablename__ = "bookings" __table_args__ = ( UniqueConstraint("when", "time_slot_id", name="uq_booking_slot"), postgresql_where=text("attendance_status <> 'cancelled'") ) 다중 컬럼에 대한 unique는 종종 사용하는 방법이었는데 where를 이용해서 조건을 설정할 수 있는 건 이번에 첨 알았습니다. 필요하면 python에서 처리하곤 했었는데 데이터베이스 기능으로 이용하는게 레이스 컨디션도 피할 수 있고 장점이 있어보이네요. 실무에 적용할때도 꼭 이용해보겠습니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 5회차 과제
파일 삭제 정책booking을 삭제할때 해당 booking과 업로드된 파일을 같이 삭제하는 정책의미없는 파일이 남아있는 것은 저장 공간 낭비유출 위험을 줄여준다(?) (예: 삭제된 booking의 파일을 남겨두어 총 100개의 파일이 저장된 상태라고 가정. 그 중에 70개는 필요한 파일, 나머지 30개는 필요없는(삭제된 booking) 파일임. 파일을 바로 제거하지 않아서 100개가 다 유출되는 것보다 의미없는 파일을 바로바로 제거하여 70개만 유출되는 것이 더(?) 낫다..)그러나 한편으로는 트래픽이 적은 시간에 삭제된 booking의 파일을 주기적으로 정리하는 로직을 실행하는 것이 더 나을까 하는 생각도 드네요
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
is_host / 캘린더 보유 여부 중에 어느 것을 사용할까? 둘 다?is_host 는 반드시 사용할 것 같음is_host 필드가 있으면 캘린더 라는 다른 엔티티에 의존하지 않고 is_host 만으로 호스트 여부를 판단할 수 있어서캘린더 보유 여부는 선택적일 것 같음아래와 같은 정책을 설정해야할 필요가 있을때is_host 인데 캘린더가 있는 경우s_host 인데 캘린더가 없는 경우 아니면 '호스트는 최소 하나 이상의 캘린더는 있어야 한다' 같은 정책
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
기획의 관점에서 Host 체크 방법is_host 필드 체크사용자가 호스트인지 먼저 확인을 하게 되므로 호스트 등록, 호스트만 조회, 호스트에 주어진 권한 제어 등 호스트를 따로 관리하고 싶을때 장점이 있습니다캘린더 보유 체크캘린더로 호스트일 경우는 호스트, 게스트 구분이 회원 등급이나 종류가 아닌 모두가 호스트나 게스트가 될수 있고 예약이 서로에게 일어날 수 있을때 장점이 있습니다. 구글 캘린더로 예를 들면 모두가 초대가 가능하고 초대도 가능한 방식입니다. 내가 선택한다면저라면 기본적으로는 is_host 필드로 체크하고 실제 예약을 받는다면 캘린더 체크를 이후에 하겠습니다. 호스트 관리를 따로 하게 되면 호스트만 따로 조회도 가능하고 호스트를 하고 싶지 않을때는 호스트가 되지 않는 것도 가능합니다. 또한 is_host를 기본 true로 하게 되면 모두가 호스트가 되는 방식도 가능하므로 앞에서 가정한 모두가 호스트/게스트인 방식도 쉽게 전환이 가능합니다. 예약 가능한 캘린더 체크는 실제로 이사람이 호스트라고 하더라도 예약일자가 있는지에 따라 예약화면 컨트롤 등이 가능하기 때문에 체크가 필요합니다. 요약하면 is_host 체크를 할 때 상황에 따라 유연하게 호스트 <-> 게스트 전환이 쉽고 필요한 경우 호스트이면서 게스트인 정책도 가능하기 때문에 좀 더 유연한 방법이라고 생각합니다.
-
미해결AWS SAA-C03 자격증 벼락치기 - 딱 163문제로 2주만에 합격하기
예상 기출 문제 오탈자 제보
제공해 주신 기출문제 PDF에서 오탈자를 발견하여 제보드립니다. p5. "분ㅅ거해" > "분석해"p51. "리로스" > "리소스"좋은 강의에 감사드립니다 : )
-
미해결[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 4회차 과제 제출
쟁점 정리예약 일정 변경과 관련해서 결정해야 할 핵심은 다음과 같습니다:일자/타임슬롯 변경을 허용할 것인가? vs 취소 후 재예약만 가능하게 할 것인가?변경을 허용한다면, 언제까지 허용할 것인가?당일 변경은 불가능하다 (이건 확정)내가 선택한 정책일자/타임슬롯 변경을 허용하되, 예약일 24시간 전까지만 가능근거:"취소 → 재예약" 방식은 사용자가 불편하고, 원하는 시간이 이미 찼을 수도 있음24시간 전까지는 게스트에게 충분한 유연성을 주면서도, 호스트가 하루 전부터 안정적으로 준비할 수 있음규칙이 단순해서 사용자가 이해하기 쉽고, 구현도 간단함구현 시나리오시나리오 1: 여유 있는 변경 (성공)민수는 2월 10일 오후 2시를 예약했습니다.2월 7일에 2월 11일 오후 4시로 바꾸고 싶어졌습니다.→ 변경 가능 (24시간 이상 여유)→ "변경이 완료되었습니다."시나리오 2: 임박한 변경 시도 (실패)지영은 2월 10일 오후 2시를 예약했습니다.2월 9일 오후 3시에 시간을 바꾸려고 합니다.→ 변경 불가 (24시간 미만)→ "예약일 24시간 전까지만 변경 가능합니다. 취소 후 재예약해주세요."시나리오 3: 당일 변경 시도 (실패)현우는 2월 10일 오후 2시를 예약했습니다.2월 10일 오전 11시에 오후 4시로 바꾸려고 합니다.→ 변경 불가 (당일)→ "당일 일정 변경은 불가능합니다."이상입니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제 제출합니다.
저는 is_host 필드와 calendar 존재 여부를 둘 다 검증해야 한다고 판단했습니다.왜 두 가지를 모두 사용해야 할까요?우선 두 요소는 서로 다른 의미를 가지고 있다고 생각합니다.is_host: 이 사용자가 호스트 자격이 있는가? (역할)calendar 존재: 이 호스트가 실제로 운영 중인가? (상태)현실로 비유하자면, is_host는 사업자등록증을 가지고 있는 상태이고, calendar가 있다는 건 실제로 가게를 오픈한 상태라고 볼 수 있습니다. 사업자등록증은 있지만 아직 가게를 열지 않은 사람도 있을 수 있잖아요?그래서 둘 중 하나만 체크하면 문제가 생긴다고 봅니다.만약 calendar 존재 여부만 체크한다면?calendar만 보고 판단하면 권한 체계가 무너질 수 있습니다. 예를 들어 일반 사용자가 억지로 캘린더를 생성하면 호스트로 둔갑할 수 있는 보안 문제가 생기죠. 또 호스트가 일시적으로 캘린더를 삭제했다가 다시 만들 때마다 권한 관리가 복잡해집니다.만약 is_host만 체크한다면?반대로 is_host만 체크하면, 캘린더가 없는데도 예약 관련 API를 호출할 수 있게 되어서 500 에러가 발생할 수 있습니다. "호스트인데 캘린더는 없는" 이상한 상태를 어떻게 처리해야 할지도 애매해지고요.두 가지를 순차적으로 검증하는 게 좋습니다저는 이렇게 단계적으로 확인하는 게 맞다고 봅니다:먼저 is_host로 호스트 자격이 있는지 확인그 다음 calendar 존재로 실제 운영 가능한지 확인이렇게 하면 사용자에게도 명확한 피드백을 줄 수 있어요. "호스트 권한이 없습니다" vs "캘린더를 먼저 생성해주세요" 처럼 무엇을 해야 하는지 정확히 알려줄 수 있죠.미래 확장성도 고려했습니다나중에 서비스가 커지면 호스트 상태를 더 세분화할 수도 있을 것 같아요:준비 중 호스트 (is_host=true, 캘린더 없음) → 대시보드 접근만 가능운영 중 호스트 (is_host=true, 캘린더 있음) → 모든 기능 사용 가능휴면 호스트 (is_host=true, 캘린더 비활성화) → 기존 예약 조회만 가능이런 식으로 관리하려면 역할과 리소스를 분리해서 체크하는 게 필수라고 생각합니다.성능 걱정은 없을까요?is_host는 User 모델의 필드라서 추가 DB 쿼리가 필요 없고, calendar 조회도 어차피 해야 하는 작업이라 성능에 큰 영향은 없다고 봅니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 2회차 과제
안녕하세요. 과제를 잘 못 이해한건가 싶기도 하지만 아래와 같은 조건하에 구현을 해 봤습니다. 수업 과정에서 Front-end는 고정인 상황이기 때문에 기존 API는 변경이 없다는 가정하에 변경 해 봤습니다. 시간대1개와 요일n개의 설정이 하나의 row로 묶여있는 상태로는 요일별 시간대 변동에 대응하기 까다로운 것 같아 요일별 개별 row로 저장하도록 일부 수정 하였습니다. API endpoint 모델 변경을 피하기 위해서 내부적으로 여러개의 요일별 row로 나누어 저장하도록 했습니다. 고민중에 작가님께서 올려 놓으신 레퍼런스 코드를 봤는데, 책에서 언급하신 것 처럼 postgres 경우와 sqlite 경우로 코드가 분기 되는 것을 보았고, 지금은 교육 과정이기 때문이라고 생각 하지만 배포 코드와 개발 환경 코드가 다른것은 여러모로 좋지 않은 것 같아 현재는 sqlite 기준으로 개별 DB구현에서만 지원하는 것은 배제하는 방향으로 구현 했습니다. 1과 같이 변경 함으로써, 기 등록된 정보의 수정에서는 개별 날짜 별 시간대 설정 면에서 자유도가 생겼다고 생각 됩니다.타임슬롯의 변경과 삭제에 관해서는 내부적으로는 time-slot으로 관리하지만, 외부공개 id는 아니므로, 키 로서 start-time, end-time, weekday (API형태로서는 리스트)조합으로 정의 해서 동작 하도록 구현했습니다.특히 삭제의 경우는 부득이 delete method의 경우는 payload가 포함되는것이 받아들여지지 않는 경우도 있는것을 고려해서, 새 등록 값 두가지(new_start_time과, new_end_time) 값이 모두 제공되고 같은 경우를 특정하여 삭제 동작으로 정의하여 동작하도록 구현했습니다. class TimeSlotUpdateByGroupIn(SQLModel): start_time: time end_time: time weekdays: Weekdays new_start_time: time | None = None new_end_time: time | None = None new_weekdays: Weekdays | None = None @model_validator(mode="after") def check_update_fields(self): update_fields = { "new_start_time": self.new_start_time, "new_end_time": self.new_end_time, "new_weekdays": self.new_weekdays, } if not any(value is not None for value in update_fields.values()): raise ValueError("최소 하나의 수정 필드는 반드시 제공되어야 합니다.") return self @router.post("/time-slots", status_code=status.HTTP_201_CREATED, response_model=TimeSlotOut) async def create_time_slot( user: CurrentUserDep, session: DbSessionDep, payload: TimeSlotCreateIn, ) -> TimeSlotOut: if not user.is_host: raise GuestPermissionError() weekdays = sorted(set(payload.weekdays)) # dup. check with already exist one stmt = select(TimeSlot).where( and_( TimeSlot.calendar_id == user.calendar.id, TimeSlot.weekday.in_(weekdays), TimeSlot.start_time < payload.end_time, TimeSlot.end_time > payload.start_time, ) ) result = await session.execute(stmt) existing_time_slot = result.scalars().first() if existing_time_slot: raise TimeSlotOverlapError() time_slots = [ TimeSlot( calendar_id=user.calendar.id, start_time=payload.start_time, end_time=payload.end_time, weekday=weekday, ) for weekday in weekdays ] session.add_all(time_slots) await session.commit() if not time_slots: raise TimeSlotOverlapError() return TimeSlotOut( start_time=time_slots[0].start_time, end_time=time_slots[0].end_time, weekdays=weekdays, created_at=time_slots[0].created_at, updated_at=time_slots[0].updated_at, ) @router.get( "/time-slots/{host_username}", status_code=status.HTTP_200_OK, response_model=list[TimeSlotOut], ) async def get_host_timeslots( host_username: str, session: DbSessionDep, ) -> list[TimeSlotOut]: stmt = ( select(User) .where(User.username == host_username) .where(User.is_host.is_(true())) ) result = await session.execute(stmt) host = result.scalar_one_or_none() if host is None or host.calendar is None: raise HostNotFoundError() stmt = select(TimeSlot).where(TimeSlot.calendar_id == host.calendar.id) result = await session.execute(stmt) time_slots = result.scalars().all() grouped: dict[tuple[time, time], TimeSlotOut] = {} for time_slot in time_slots: key = (time_slot.start_time, time_slot.end_time) if key not in grouped: grouped[key] = TimeSlotOut( start_time=time_slot.start_time, end_time=time_slot.end_time, weekdays=[time_slot.weekday], created_at=time_slot.created_at, updated_at=time_slot.updated_at, ) continue grouped[key].weekdays.append(time_slot.weekday) if time_slot.created_at < grouped[key].created_at: grouped[key].created_at = time_slot.created_at if time_slot.updated_at > grouped[key].updated_at: grouped[key].updated_at = time_slot.updated_at if not grouped: raise TimeSlotNotFoundError() for time_slot in grouped.values(): time_slot.weekdays = sorted(set(time_slot.weekdays)) return list(grouped.values()) @router.patch( "/time-slots", status_code=status.HTTP_200_OK, response_model=TimeSlotOut | None, ) async def update_time_slot( user: CurrentUserDep, session: DbSessionDep, payload: TimeSlotUpdateByGroupIn, ) -> TimeSlotOut | None: if not user.is_host: raise GuestPermissionError() current_weekdays = sorted(set(payload.weekdays)) stmt = select(TimeSlot).where( and_( TimeSlot.calendar_id == user.calendar.id, TimeSlot.start_time == payload.start_time, TimeSlot.end_time == payload.end_time, ) ) result = await session.execute(stmt) current_slots = result.scalars().all() if not current_slots: raise TimeSlotNotFoundError() existing_weekdays = sorted({slot.weekday for slot in current_slots}) if existing_weekdays != current_weekdays: raise TimeSlotNotFoundError() delete_only = ( payload.new_start_time is not None and payload.new_end_time is not None and payload.new_start_time == payload.new_end_time ) if delete_only: current_ids = [slot.id for slot in current_slots] await session.execute(delete(TimeSlot).where(TimeSlot.id.in_(current_ids))) await session.commit() return None new_start_time = payload.new_start_time or payload.start_time new_end_time = payload.new_end_time or payload.end_time new_weekdays = sorted(set(payload.new_weekdays or payload.weekdays)) if new_start_time >= new_end_time: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail="시작 시간은 종료 시간보다 빨라야 합니다.", ) current_ids = [slot.id for slot in current_slots] stmt = select(TimeSlot).where( and_( TimeSlot.calendar_id == user.calendar.id, TimeSlot.weekday.in_(new_weekdays), TimeSlot.start_time < new_end_time, TimeSlot.end_time > new_start_time, TimeSlot.id.not_in(current_ids), ) ) result = await session.execute(stmt) existing_time_slot = result.scalars().first() if existing_time_slot: raise TimeSlotOverlapError() await session.execute(delete(TimeSlot).where(TimeSlot.id.in_(current_ids))) new_time_slots = [ TimeSlot( calendar_id=user.calendar.id, start_time=new_start_time, end_time=new_end_time, weekday=weekday, ) for weekday in new_weekdays ] session.add_all(new_time_slots) await session.commit() return TimeSlotOut( start_time=new_start_time, end_time=new_end_time, weekdays=new_weekdays, created_at=new_time_slots[0].created_at, updated_at=new_time_slots[0].updated_at, )
-
해결됨Flutter로 웹 서비스 개발하기 (1) - Responsive Web
SEO에 대해
안녕하세요.우선 강의 정말 잘 수강했습니다. https://pub.dev/packages/flutter_seo해당 커뮤니티를 보다가 댓글에서 해당 패키지를 찾게되었고강사님도 해당 seo패키지를 사용해서 seo적용을 하신것같은데,지금 배포된 샐링잇 웹사이트도 해당 seo패키지만을 적용해서 배포를 하신건지 궁금합니다. 그러면 기존 웹사이트 seo만큼은 아니지만, 저 패키지를 적용하면 회사이름(고유명사) 정도는 구글에 치면 나올 정도로는 적용을 할 수 있는건지 궁금합니다.
-
미해결쉽게 설명하는 AWS 기초 강의
개인 블로그에 올려도되나요?
안녕하세요혹시 강의 PDF 일부 캡처본(1~2개정도)를 학습 기록용 블로그에 올려도 되는지 궁금합니다
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
patch 요청시 payload가 넘어가지 않습니다.
아래 두 요청 코드에서 patch 요청 시 payload가 엔드포인트 함수에서 None으로 잡혀 model_dump()에서 오류가 발생합니다. 원인을 잘 모르겠습니다.[오류][요청 코드]@pytest.mark.parametrize("payload", [ {"display_name": "푸딩캠프"}, {"email": "hannal@example.com"}, {"display_name": "푸딩캠프", "email": "hannal@example.com"}, ]) async def test_사용자가_변경하는_항목만_변경되고_나머지는_기존_값을_유지한다( client_with_auth: TestClient, # 인증을 받은 클라이언트 payload: dict, # 클라이언트 요청 페이로드 host_user: User, # 클라이언트 사용자 ): # 현재 사용자 정보를 보관한다. before_data = host_user.model_dump() response = client_with_auth.patch("/account/@me", json=payload) # (...)async def test_비밀번호_변경_시_해싱_처리한_비밀번호가_저장되어야_한다( client_with_auth: TestClient, host_user: User, db_session: AsyncSession, ): before_data = host_user.hashed_password payload = { "password": "new_password", "password_again": "new_password", } response = client_with_auth.patch("/account/@me", json=payload) # (...)[엔드포인트]@router.patch("/@me", response_model=UserDetailOut) async def update_user( user: CurrentUserDep, session: DbSessionDep, payload: UpdateUserPayload = Body(...), ) -> User: updated_data = payload.model_dump(exclude_none=True, exclude={"password", "password_again"}) stmt = update(User).where(User.id == user.id).values(**updated_data) await session.execute(stmt) await session.commit() await session.refresh(user) return user [스키마]class UpdateUserPayload(SQLModel): display_name: str | None = Field(default=None, min_length=4, max_length=40) email: EmailStr | None = Field(default=None, max_length=128) password: str | None = Field(default=None, min_length=8, max_length=128) password_again: str | None = Field(default=None, min_length=8, max_length=128) @model_validator(mode="after") def check_all_fields_are_none(self) -> Self: if not self.model_dump(exclude_none=True): raise ValueError("최소 하나의 필드는 반드시 제공되어야 합니다.") return self @model_validator(mode="after") def verify_password(self) -> Self: if self.password is not None or self.password_again is not None: # 둘 중 하나라도 들어오면 둘 다 있어야 함 if not self.password or not self.password_again: raise ValueError("비밀번호 변경 시 password와 password_again을 모두 제공해야 합니다.") if self.password != self.password_again: raise ValueError("비밀번호가 일치하지 않습니다.") @computed_field @property def hashed_password(self) -> str | None: if self.password: return hash_password(self.password) return None[픽스처]@pytest.fixture(autouse=True) async def db_session(): dsn = "sqlite+aiosqlite:///:memory:" engine = create_async_engine(dsn) async with engine.begin() as conn: await conn.run_sync(SQLModel.metadata.drop_all) await conn.run_sync(SQLModel.metadata.create_all) session_factory = create_session(engine) async with session_factory() as session: yield session await conn.run_sync(SQLModel.metadata.drop_all) await engine.dispose() @pytest.fixture() def fastapi_app(db_session: AsyncSession): app = FastAPI() include_routers(app) async def override_use_session(): yield db_session app.dependency_overrides[use_session] = override_use_session return app @pytest.fixture() async def host_user(db_session: AsyncSession): user = account_models.User( username="puddingcamp", hashed_password=hash_password("testtest"), email="puddingcamp@example.com", display_name="푸딩캠프", is_host=True, ) db_session.add(user) await db_session.flush() await db_session.commit() return user @pytest.fixture() def client_with_auth(fastapi_app: FastAPI, host_user: account_models.User): payload = LoginPayload.model_validate({ "username": host_user.username, "password": "testtest", }) with TestClient(fastapi_app) as client: response = client.post("/account/login", json=payload.model_dump()) assert response.status_code == status.HTTP_200_OK auth_token = response.cookies.get("auth_token") assert auth_token is not None client.cookies.set("auth_token", auth_token) yield client
-
미해결비전공자도 이해할 수 있는 Nginx 입문/실전
HTTPS 관련 코드 해석하기
안녕하세요. 섹션 7의 "35. Nginx, Certbot이 작성한 HTTPS 관련 코드 해석하기" 강의를 들으면서 궁금한 점이 생겨서 질문 남깁니다. server { # 2. 들어온 요청의 주소의 Host가 jscode.p-e.kr일 경우 (ex. http://jscode.p-e.kr/about) # https://jscode.p-e.kr/...(ex. https://jscode.p-e.kr/about)로 리다이렉트(301) 처리 if ($host = jscode.p-e.kr) { return 301 https://$host$request_uri; } # managed by Certbot # 1. jscode.p-e.kr 주소로 들어온 요청이면서 # 80번 포트(http)로 들어오는 요청일 때 # 이 server 블럭에서 처리하도록 설정 listen 80; server_name jscode.p-e.kr; # 3. 그 이외의 경우에는 404(Not Found)로 응답 return 404; # managed by Certbot } 강의자료에서 제공해주시는 /etc/nginx/conf.d/default.conf 파일의 일부 코드입니다.이미 server_name에서 jscode.p-e.kr 주소로 들어온 요청인지 확인하니깐, if( $host = jscode.p-e.kr) 는 없어도 되는거 아닌가요? 코드가 존재하는 다른 의도가 있는건지 궁금합니다.
-
해결됨CloudNet@ - Amazon EKS 기본 강의
강의 연장 부탁드립니다.
안녕하세요. 해당 강의 수강할 수 있는 기간이 있던 걸로 기억해서 확인해보니 작년 2월 3일에 구매했더라구요.아직 완강하지 못해서 강의 연장을 요청 드립니다. 확인 부탁드리겠습니다.감사합니다.
-
미해결비전공자도 이해할 수 있는 Nginx 입문/실전
default.conf 파일이 없습니다.
/etc/nginx/conf.d 폴더에 default.conf 파일이 없습니다.
-
미해결스프링 부트와 리액트로 구현하는 보안 JWT 로그인
섹션 9 실전 질문
강사님 세션 9에서 Secretes Manager나 Parameter Store 를 현업에서도 깃허브 액션 하고 같이 쓰나요?이거 궁금해서 질문드립니다.
-
미해결Golang을 통한 백엔드 개발 및 환경 구축하기
프로젝트 구조 관련 질문이 있습니다.
안녕하세요!! 클라우드 관련 공부하다가 go에 관심이 생겨 수강하게 되었습니다.백엔드는 이 강의와 책으로 처음 공부하는 중입니다.현재는 강의 프로젝트 구조로 개인 프로젝트 진행해보려고 리펙터링 중 입니다. 수업과 관련은 없지만 3가지 질문이 있습니다.1. 트랜잭션 구현을 위한 *sql.DB는 어디서 생성하고 어떻게 service에 주입해야 하나요?reposiory는 수정해서, *sql.DB 필드 하나만 위치하도록 했습니다. 다만, 트랜잭션 구현을 하다보니, db 커넥션 풀을 repository에서 service로 가져오거나, cmd에서부터 service까지 내려보내주어야 하더라고요. cmd에 db 커넥션 풀을 생성하도록 하면 안되는 걸까요? orm 도입하기 이전에 내장 패키지로 기본 로직을 구현하려는 건 쓸 데 없는 일일까요?2. 프로젝트 구조를 어째서 cmd에 전부 주입하는 식으로 작성하셨나요? 테스트는 어떻게 하시나요?cmd를 main패키지 처럼 사용하시는 건지 궁금합니다. 제가 알고 있던 백엔드 구조(spring boot)와 많이 달라 보이네요. 또, 이 구조에서 테스트는 어떻게 진행해야 할지 잘 모르겠습니다. 대략적인 흐름이라도 알고자 여쭤봅니다.3. 일하실 때에도 이 구조를 유지하시나요? 도메인별 패키지 분리(user, auth, member... etc)는 별로인가요?개인적으로는 비즈니스 도메인별로 디렉터리를 나누는 것을 선호합니다. 다만, 현재 이 디렉터리 구조를 뜯어고칠 용기가 생기지는 않네요... 이 구조의 어떤 점 때문에 이렇게 사용하시는 지, 혹은 팀에서 이렇게 쓰길래 유지를 하고 계신건지 궁금합니다. 장단점이 있을까요?궁금한걸 쌓아두면서 gpt로 해결하려다가, 쌓아둔게 터진 것처럼 장황하네요.. 강의를 들으면서 해결 못한 내용이라 그런 것 같아요. 좋은 강의 잘 듣고 있습니다 🥹🙏
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
- is_host를 기준으로 호스트 사용자와 게스트 사용자를 구분하는 것은, 가입 당시부터 본인의 역할을 선택하여 구분 짓는 경우에 효과적인 것으로 판단됩니다. 가입 시점부터 역할(게스트/호스트)을 구분 지어서 관리하고자 하는 경우에 적절한 구조라고 생각되며, 호스트의 자격조건이 있거나, 검증이 필요한 서비스라면 이렇게 관리하는 방법이 적절해 보입니다. - 반면, 호스트/게스트의 타입을 캘린더의 존재 여부(또는 갯수)로 정의한다고 하면, 모든 가입자가 가입시에 동일한 자격을 갖는 가입자로서 가입처리가 되고, 추가 단계로서 캘린더를 생성 함으로 써, 호스트와 게스트의 역할을 자유롭게 넘나드는 자유도가 생기는 구조가 될 것 같습니다. - 지금은 커피챗을 목적으로 일정을 조율하는 목적을 갖는 시스템이므로, is_host를 사용하지 않는 결정을 하겠습니다. 전자상거래 시스템같은 판매자와/소비자 처럼 엄격한 구분과 자격을 검증해야 하는 시스템으로 여겨지지는 않기때문입니다. is_host 필드가 없어지는 경우에, 사용자의 상태(또는 역할)을 구분하기 위해서 매번 캘린더의 존재 여부를 확인해야 하지만 앞서 언급한 호스트와 게스트의 역할 변경에 열려있는 점이 중요하게 생각되기 때문입니다. 하지만 모든 가입자가 게스트로 시작하는 만큼 커피챗 호스트를 어떻게 유치할 것인가는 고민이 되는 부분입니다.
-
미해결비전공자도 이해할 수 있는 CI/CD 입문·실전
CodeDeploy 사용시 registration 요구
CodeDeploy 를 클릭했더니, 이런 문구가 뜨네요.complete your AWS registration 눌러도 콘솔 홈으로 돌아가고,credit card information 을 눌렀더니, 결제 기본 설정 창이 뜨는데, 딱히 문제될 건 없어 보여요.다른 AWS 서비스 사용할 때는 이런 문구 안 떴는데 왜 CodeDeploy만 이럴까요? ㅠㅠ
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
페이지 144 코드 문의
def create_engine(dsn: str): return create_async_engine( dsn, echo=True, # SQLAlchemy가 실행하는 SQL을 콘솔에 출력하도록 함 ) def create_session(async_engine: AsyncEngine | None = None): if async_engine is None: async_engine = create_engine()위 코드중 마지막줄async_engine = create_engine() 여기에 dsn 인자가 없어서 오류 나지 않나요?
-
미해결비전공자도 이해할 수 있는 CI/CD 입문·실전
스프링 종료 명령어
안녕하세요 sudo fuser -k -n tcp 8080 이 명령어로 스프링 죽이시던데 실제 실무에서도 저 명령어로 스크립트를 구성하나요?